summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp54
-rw-r--r--Android.bp19
-rw-r--r--INPUT_OWNERS2
-rw-r--r--Ravenwood.bp13
-rw-r--r--TEST_MAPPING11
-rw-r--r--THERMAL_OWNERS3
-rw-r--r--apct-tests/perftests/OWNERS5
-rw-r--r--apct-tests/perftests/core/Android.bp1
-rw-r--r--apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpgbin0 -> 3579758 bytes
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java43
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java2
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java2
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java3
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java25
-rw-r--r--apex/jobscheduler/framework/aconfig/job.aconfig8
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java198
-rw-r--r--apex/jobscheduler/service/Android.bp5
-rw-r--r--apex/jobscheduler/service/aconfig/Android.bp17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java18
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java62
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java314
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java161
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java81
-rw-r--r--api/Android.bp7
-rw-r--r--api/StubLibraries.bp1
-rw-r--r--api/api.go3
-rw-r--r--api/coverage/tools/Android.bp32
-rw-r--r--api/coverage/tools/ExtractFlaggedApis.kt58
-rw-r--r--api/coverage/tools/extract_flagged_apis.proto34
-rw-r--r--api/javadoc-lint-baseline10
-rw-r--r--cmds/screencap/screencap.cpp44
-rw-r--r--cmds/uinput/jni/com_android_commands_uinput_Device.cpp5
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Device.java9
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java469
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java334
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Uinput.java8
-rw-r--r--core/api/current.txt142
-rw-r--r--core/api/lint-baseline.txt42
-rw-r--r--core/api/module-lib-current.txt9
-rw-r--r--core/api/removed.txt22
-rw-r--r--core/api/system-current.txt203
-rw-r--r--core/api/system-lint-baseline.txt12
-rw-r--r--core/api/system-removed.txt52
-rw-r--r--core/api/test-current.txt3
-rw-r--r--core/api/test-lint-baseline.txt10
-rw-r--r--core/java/Android.bp4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java1
-rw-r--r--core/java/android/accessibilityservice/TouchInteractionController.java3
-rw-r--r--core/java/android/app/Activity.java8
-rw-r--r--core/java/android/app/ActivityManager.java1
-rw-r--r--core/java/android/app/ActivityOptions.java3
-rw-r--r--core/java/android/app/ActivityThread.java13
-rw-r--r--core/java/android/app/AppOpsManager.java5
-rw-r--r--core/java/android/app/AutomaticZenRule.java85
-rw-r--r--core/java/android/app/ContextImpl.java3
-rw-r--r--core/java/android/app/DownloadManager.java5
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/Instrumentation.java41
-rw-r--r--core/java/android/app/KeyguardManager.java3
-rw-r--r--core/java/android/app/Notification.java1
-rw-r--r--core/java/android/app/NotificationManager.java69
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/app/WindowConfiguration.java4
-rw-r--r--core/java/android/app/admin/DeviceAdminInfo.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java58
-rw-r--r--core/java/android/app/admin/DevicePolicyResourcesManager.java2
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEvent.java8
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextManager.java6
-rw-r--r--core/java/android/app/backup/BackupAgent.java8
-rw-r--r--core/java/android/app/cloudsearch/SearchResponse.java3
-rw-r--r--core/java/android/app/notification.aconfig7
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java8
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java2
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java10
-rw-r--r--core/java/android/app/servertransaction/ActivityResultItem.java3
-rw-r--r--core/java/android/app/servertransaction/ActivityTransactionItem.java4
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java5
-rw-r--r--core/java/android/app/servertransaction/ClientTransactionItem.java2
-rw-r--r--core/java/android/app/servertransaction/ConfigurationChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java15
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java4
-rw-r--r--core/java/android/app/servertransaction/NewIntentItem.java3
-rw-r--r--core/java/android/app/servertransaction/WindowContextInfoChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/WindowStateResizeItem.java6
-rw-r--r--core/java/android/app/wearable/WearableSensingManager.java6
-rw-r--r--core/java/android/appwidget/flags.aconfig8
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java28
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl14
-rw-r--r--core/java/android/companion/virtual/VirtualDevice.java5
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java10
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java18
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java6
-rw-r--r--core/java/android/companion/virtual/camera/IVirtualCamera.aidl17
-rw-r--r--core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl70
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCamera.java57
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraCallback.java53
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl (renamed from core/java/android/companion/virtual/camera/VirtualCameraSession.java)15
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraConfig.java233
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl12
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl (renamed from core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl)12
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraMetadata.java61
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java44
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl8
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java106
-rw-r--r--core/java/android/companion/virtual/flags.aconfig7
-rw-r--r--core/java/android/content/ClipData.java10
-rw-r--r--core/java/android/content/ClipDescription.java1
-rw-r--r--core/java/android/content/ComponentName.java1
-rw-r--r--core/java/android/content/ContentUris.java1
-rw-r--r--core/java/android/content/ContentValues.java1
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/content/Intent.java27
-rw-r--r--core/java/android/content/IntentFilter.java1
-rw-r--r--core/java/android/content/TEST_MAPPING8
-rw-r--r--core/java/android/content/UriMatcher.java1
-rw-r--r--core/java/android/content/pm/Checksum.java11
-rw-r--r--core/java/android/content/pm/LauncherActivityInfo.java172
-rw-r--r--core/java/android/content/pm/PackageInfo.java31
-rw-r--r--core/java/android/content/pm/PackageInstaller.java40
-rw-r--r--core/java/android/content/pm/PackageManager.java57
-rw-r--r--core/java/android/content/pm/SigningInfo.java6
-rw-r--r--core/java/android/content/pm/UserProperties.java57
-rw-r--r--core/java/android/content/pm/flags.aconfig7
-rw-r--r--core/java/android/content/pm/parsing/ApkLite.java21
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java23
-rw-r--r--core/java/android/content/res/Configuration.java2
-rw-r--r--core/java/android/content/res/Element.java22
-rw-r--r--core/java/android/content/res/Validator.java20
-rw-r--r--core/java/android/credentials/flags.aconfig7
-rw-r--r--core/java/android/database/AbstractCursor.java22
-rw-r--r--core/java/android/database/CharArrayBuffer.java1
-rw-r--r--core/java/android/database/ContentObservable.java1
-rw-r--r--core/java/android/database/ContentObserver.java13
-rw-r--r--core/java/android/database/Cursor.java1
-rw-r--r--core/java/android/database/CursorIndexOutOfBoundsException.java1
-rw-r--r--core/java/android/database/CursorJoiner.java1
-rw-r--r--core/java/android/database/CursorWrapper.java1
-rw-r--r--core/java/android/database/DataSetObservable.java1
-rw-r--r--core/java/android/database/DataSetObserver.java1
-rw-r--r--core/java/android/database/MatrixCursor.java1
-rw-r--r--core/java/android/database/MergeCursor.java1
-rw-r--r--core/java/android/database/Observable.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteRawStatement.java38
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java1
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java1
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java7
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java8
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java8
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionUtils.java7
-rw-r--r--core/java/android/hardware/camera2/impl/FrameNumberTracker.java16
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java15
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java52
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java4
-rw-r--r--core/java/android/hardware/input/VirtualDpad.java10
-rw-r--r--core/java/android/hardware/input/VirtualInputDevice.java14
-rw-r--r--core/java/android/hardware/input/VirtualInputDeviceConfig.java85
-rw-r--r--core/java/android/hardware/input/VirtualKeyEvent.java9
-rw-r--r--core/java/android/hardware/input/VirtualKeyboard.java11
-rw-r--r--core/java/android/hardware/input/VirtualKeyboardConfig.java6
-rw-r--r--core/java/android/hardware/input/VirtualMouse.java20
-rw-r--r--core/java/android/hardware/input/VirtualMouseButtonEvent.java8
-rw-r--r--core/java/android/hardware/input/VirtualMouseRelativeEvent.java8
-rw-r--r--core/java/android/hardware/input/VirtualMouseScrollEvent.java8
-rw-r--r--core/java/android/hardware/input/VirtualNavigationTouchpad.java11
-rw-r--r--core/java/android/hardware/input/VirtualNavigationTouchpadConfig.java6
-rw-r--r--core/java/android/hardware/input/VirtualTouchEvent.java13
-rw-r--r--core/java/android/hardware/input/VirtualTouchscreen.java11
-rw-r--r--core/java/android/hardware/input/VirtualTouchscreenConfig.java6
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java12
-rw-r--r--core/java/android/hardware/radio/ProgramSelector.java6
-rw-r--r--core/java/android/hardware/radio/RadioManager.java1
-rw-r--r--core/java/android/hardware/usb/UsbManager.java5
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java7
-rw-r--r--core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig7
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java5
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java11
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java17
-rw-r--r--core/java/android/net/INetworkManagementEventObserver.aidl4
-rw-r--r--core/java/android/net/NetworkStack.java13
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl2
-rw-r--r--core/java/android/nfc/INfcCardEmulation.aidl1
-rw-r--r--core/java/android/nfc/NfcAdapter.java55
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java6
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java18
-rw-r--r--core/java/android/nfc/cardemulation/HostApduService.java118
-rw-r--r--core/java/android/nfc/flags.aconfig28
-rw-r--r--core/java/android/os/BatteryUsageStats.java1
-rw-r--r--core/java/android/os/Binder.java96
-rw-r--r--core/java/android/os/DeadObjectException.java24
-rw-r--r--core/java/android/os/DeadSystemRuntimeException.java9
-rw-r--r--core/java/android/os/IBinder.java1
-rw-r--r--core/java/android/os/IThermalService.aidl5
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl4
-rw-r--r--core/java/android/os/OWNERS4
-rw-r--r--core/java/android/os/PowerManager.java64
-rw-r--r--core/java/android/os/Process.java48
-rw-r--r--core/java/android/os/RecoverySystem.java3
-rw-r--r--core/java/android/os/RevocableFileDescriptor.java19
-rw-r--r--core/java/android/os/SystemClock.java39
-rw-r--r--core/java/android/os/SystemVibratorManager.java8
-rw-r--r--core/java/android/os/TEST_MAPPING6
-rw-r--r--core/java/android/os/UidBatteryConsumer.java71
-rw-r--r--core/java/android/os/UpdateEngine.java4
-rw-r--r--core/java/android/os/UserHandle.java1
-rw-r--r--core/java/android/os/UserManager.java31
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/os/storage/StorageManager.java1
-rw-r--r--core/java/android/permission/IOnPermissionsChangeListener.aidl2
-rw-r--r--core/java/android/permission/PermissionManager.java9
-rw-r--r--core/java/android/permission/flags.aconfig8
-rw-r--r--core/java/android/provider/Settings.java19
-rw-r--r--core/java/android/provider/Telephony.java16
-rw-r--r--core/java/android/security/flags.aconfig7
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig22
-rw-r--r--core/java/android/service/notification/Condition.java104
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java2
-rw-r--r--core/java/android/service/notification/ZenDeviceEffects.java370
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java102
-rw-r--r--core/java/android/service/notification/ZenModeDiff.java35
-rw-r--r--core/java/android/service/persistentdata/PersistentDataBlockManager.java1
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java1
-rw-r--r--core/java/android/service/voice/HotwordRejectedResult.java4
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java6
-rw-r--r--core/java/android/speech/SpeechRecognizer.java63
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/text/TextUtils.java55
-rw-r--r--core/java/android/text/flags/flags.aconfig7
-rw-r--r--core/java/android/util/DataUnit.java1
-rw-r--r--core/java/android/util/EventLog.java23
-rw-r--r--core/java/android/util/FeatureFlagUtils.java7
-rw-r--r--core/java/android/util/IntArray.java1
-rw-r--r--core/java/android/util/LongArray.java1
-rw-r--r--core/java/android/util/Slog.java6
-rw-r--r--core/java/android/util/TEST_MAPPING6
-rw-r--r--core/java/android/util/TimeUtils.java19
-rw-r--r--core/java/android/util/Xml.java68
-rw-r--r--core/java/android/util/proto/TEST_MAPPING6
-rw-r--r--core/java/android/view/IWindowManager.aidl2
-rw-r--r--core/java/android/view/IWindowSession.aidl17
-rw-r--r--core/java/android/view/InputEventReceiver.java10
-rw-r--r--core/java/android/view/InputWindowHandle.java35
-rw-r--r--core/java/android/view/InsetsSource.java3
-rw-r--r--core/java/android/view/KeyEvent.java1
-rw-r--r--core/java/android/view/SurfaceControl.java8
-rw-r--r--core/java/android/view/SurfaceView.java21
-rw-r--r--core/java/android/view/View.java79
-rw-r--r--core/java/android/view/ViewConfiguration.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java92
-rw-r--r--core/java/android/view/WindowManager.java18
-rw-r--r--core/java/android/view/WindowManagerGlobal.java11
-rw-r--r--core/java/android/view/WindowlessWindowManager.java19
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java8
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig14
-rw-r--r--core/java/android/view/inputmethod/HandwritingGesture.java1
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java15
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig10
-rw-r--r--core/java/android/window/BackNavigationInfo.java4
-rw-r--r--core/java/android/window/ClientWindowFrames.java25
-rw-r--r--core/java/android/window/ScreenCapture.java14
-rw-r--r--core/java/android/window/SnapshotDrawerUtils.java3
-rw-r--r--core/java/android/window/SplashScreen.java3
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig17
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig8
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig7
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java7
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java139
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java48
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java4
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java12
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java4
-rw-r--r--core/java/com/android/internal/jank/DisplayRefreshRate.java86
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java31
-rw-r--r--core/java/com/android/internal/net/VpnProfile.java10
-rw-r--r--core/java/com/android/internal/os/ProcessCpuTracker.java6
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedActivity.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java)15
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedComponent.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedPermission.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedProcess.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedProvider.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedService.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedService.java)2
-rw-r--r--core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java (renamed from services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java)2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java9
-rw-r--r--core/java/com/android/internal/util/ArtFastDataInput.java15
-rw-r--r--core/java/com/android/internal/util/ArtFastDataOutput.java14
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java3
-rw-r--r--core/java/com/android/server/net/BaseNetworkObserver.java2
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp12
-rw-r--r--core/jni/android_media_AudioRecord.cpp7
-rw-r--r--core/jni/android_util_Process.cpp2
-rw-r--r--core/jni/android_view_SurfaceControl.cpp8
-rw-r--r--core/jni/android_window_ScreenCapture.cpp6
-rw-r--r--core/jni/jni_common.cpp14
-rw-r--r--core/jni/jni_common.h3
-rw-r--r--core/res/Android.bp2
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/OWNERS2
-rw-r--r--core/res/res/drawable-nodpi/ic_thermostat_notification.xml29
-rw-r--r--core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml8
-rw-r--r--core/res/res/drawable/ic_accessibility_generic.xml30
-rw-r--r--core/res/res/layout/accessibility_service_warning.xml137
-rw-r--r--core/res/res/values-af-watch/strings.xml6
-rw-r--r--core/res/res/values-af/strings.xml9
-rw-r--r--core/res/res/values-am-watch/strings.xml6
-rw-r--r--core/res/res/values-am/strings.xml10
-rw-r--r--core/res/res/values-ar-watch/strings.xml6
-rw-r--r--core/res/res/values-ar/strings.xml12
-rw-r--r--core/res/res/values-as-watch/strings.xml6
-rw-r--r--core/res/res/values-as/strings.xml10
-rw-r--r--core/res/res/values-az-watch/strings.xml6
-rw-r--r--core/res/res/values-az/strings.xml10
-rw-r--r--core/res/res/values-b+sr+Latn-watch/strings.xml6
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml10
-rw-r--r--core/res/res/values-be-watch/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml10
-rw-r--r--core/res/res/values-bg-watch/strings.xml6
-rw-r--r--core/res/res/values-bg/strings.xml10
-rw-r--r--core/res/res/values-bn-watch/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml10
-rw-r--r--core/res/res/values-bs-watch/strings.xml6
-rw-r--r--core/res/res/values-bs/strings.xml10
-rw-r--r--core/res/res/values-ca-watch/strings.xml6
-rw-r--r--core/res/res/values-ca/strings.xml18
-rw-r--r--core/res/res/values-cs-watch/strings.xml6
-rw-r--r--core/res/res/values-cs/strings.xml10
-rw-r--r--core/res/res/values-da-watch/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml10
-rw-r--r--core/res/res/values-de-watch/strings.xml6
-rw-r--r--core/res/res/values-de/strings.xml12
-rw-r--r--core/res/res/values-el-watch/strings.xml6
-rw-r--r--core/res/res/values-el/strings.xml10
-rw-r--r--core/res/res/values-en-rAU-watch/strings.xml6
-rw-r--r--core/res/res/values-en-rAU/strings.xml10
-rw-r--r--core/res/res/values-en-rCA/strings.xml7
-rw-r--r--core/res/res/values-en-rGB-watch/strings.xml6
-rw-r--r--core/res/res/values-en-rGB/strings.xml10
-rw-r--r--core/res/res/values-en-rIN-watch/strings.xml6
-rw-r--r--core/res/res/values-en-rIN/strings.xml10
-rw-r--r--core/res/res/values-en-rXC/strings.xml7
-rw-r--r--core/res/res/values-es-rUS-watch/strings.xml6
-rw-r--r--core/res/res/values-es-rUS/strings.xml10
-rw-r--r--core/res/res/values-es-watch/strings.xml6
-rw-r--r--core/res/res/values-es/strings.xml10
-rw-r--r--core/res/res/values-et-watch/strings.xml6
-rw-r--r--core/res/res/values-et/strings.xml10
-rw-r--r--core/res/res/values-eu/strings.xml10
-rw-r--r--core/res/res/values-fa-watch/strings.xml6
-rw-r--r--core/res/res/values-fa/strings.xml12
-rw-r--r--core/res/res/values-fi-watch/strings.xml6
-rw-r--r--core/res/res/values-fi/strings.xml10
-rw-r--r--core/res/res/values-fr-rCA-watch/strings.xml6
-rw-r--r--core/res/res/values-fr-rCA/strings.xml10
-rw-r--r--core/res/res/values-fr-watch/strings.xml6
-rw-r--r--core/res/res/values-fr/strings.xml20
-rw-r--r--core/res/res/values-gl-watch/strings.xml6
-rw-r--r--core/res/res/values-gl/strings.xml10
-rw-r--r--core/res/res/values-gu-watch/strings.xml6
-rw-r--r--core/res/res/values-gu/strings.xml12
-rw-r--r--core/res/res/values-hi-watch/strings.xml6
-rw-r--r--core/res/res/values-hi/strings.xml10
-rw-r--r--core/res/res/values-hr-watch/strings.xml6
-rw-r--r--core/res/res/values-hr/strings.xml12
-rw-r--r--core/res/res/values-hu-watch/strings.xml6
-rw-r--r--core/res/res/values-hu/strings.xml10
-rw-r--r--core/res/res/values-hy-watch/strings.xml6
-rw-r--r--core/res/res/values-hy/strings.xml10
-rw-r--r--core/res/res/values-in-watch/strings.xml6
-rw-r--r--core/res/res/values-in/strings.xml12
-rw-r--r--core/res/res/values-is-watch/strings.xml6
-rw-r--r--core/res/res/values-is/strings.xml10
-rw-r--r--core/res/res/values-it-watch/strings.xml6
-rw-r--r--core/res/res/values-it/strings.xml10
-rw-r--r--core/res/res/values-iw-watch/strings.xml6
-rw-r--r--core/res/res/values-iw/strings.xml10
-rw-r--r--core/res/res/values-ja-watch/strings.xml6
-rw-r--r--core/res/res/values-ja/strings.xml12
-rw-r--r--core/res/res/values-ka-watch/strings.xml6
-rw-r--r--core/res/res/values-ka/strings.xml10
-rw-r--r--core/res/res/values-kk-watch/strings.xml6
-rw-r--r--core/res/res/values-kk/strings.xml12
-rw-r--r--core/res/res/values-km-watch/strings.xml6
-rw-r--r--core/res/res/values-km/strings.xml10
-rw-r--r--core/res/res/values-kn-watch/strings.xml6
-rw-r--r--core/res/res/values-kn/strings.xml14
-rw-r--r--core/res/res/values-ko-watch/strings.xml6
-rw-r--r--core/res/res/values-ko/strings.xml12
-rw-r--r--core/res/res/values-ky-watch/strings.xml6
-rw-r--r--core/res/res/values-ky/strings.xml14
-rw-r--r--core/res/res/values-lo-watch/strings.xml6
-rw-r--r--core/res/res/values-lo/strings.xml10
-rw-r--r--core/res/res/values-lt-watch/strings.xml6
-rw-r--r--core/res/res/values-lt/strings.xml10
-rw-r--r--core/res/res/values-lv-watch/strings.xml6
-rw-r--r--core/res/res/values-lv/strings.xml10
-rw-r--r--core/res/res/values-mk-watch/strings.xml6
-rw-r--r--core/res/res/values-mk/strings.xml10
-rw-r--r--core/res/res/values-ml-watch/strings.xml6
-rw-r--r--core/res/res/values-ml/strings.xml10
-rw-r--r--core/res/res/values-mn-watch/strings.xml6
-rw-r--r--core/res/res/values-mn/strings.xml10
-rw-r--r--core/res/res/values-mr-watch/strings.xml6
-rw-r--r--core/res/res/values-mr/strings.xml9
-rw-r--r--core/res/res/values-ms-watch/strings.xml6
-rw-r--r--core/res/res/values-ms/strings.xml10
-rw-r--r--core/res/res/values-my-watch/strings.xml6
-rw-r--r--core/res/res/values-my/strings.xml14
-rw-r--r--core/res/res/values-nb-watch/strings.xml6
-rw-r--r--core/res/res/values-nb/strings.xml10
-rw-r--r--core/res/res/values-ne-watch/strings.xml6
-rw-r--r--core/res/res/values-ne/strings.xml14
-rw-r--r--core/res/res/values-nl-watch/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml10
-rw-r--r--core/res/res/values-or-watch/strings.xml6
-rw-r--r--core/res/res/values-or/strings.xml14
-rw-r--r--core/res/res/values-pa-watch/strings.xml6
-rw-r--r--core/res/res/values-pa/strings.xml14
-rw-r--r--core/res/res/values-pl-watch/strings.xml6
-rw-r--r--core/res/res/values-pl/strings.xml12
-rw-r--r--core/res/res/values-pt-rBR-watch/strings.xml6
-rw-r--r--core/res/res/values-pt-rBR/strings.xml14
-rw-r--r--core/res/res/values-pt-rPT-watch/strings.xml6
-rw-r--r--core/res/res/values-pt-rPT/strings.xml10
-rw-r--r--core/res/res/values-pt-watch/strings.xml6
-rw-r--r--core/res/res/values-pt/strings.xml14
-rw-r--r--core/res/res/values-ro-watch/strings.xml6
-rw-r--r--core/res/res/values-ro/strings.xml10
-rw-r--r--core/res/res/values-ru-watch/strings.xml6
-rw-r--r--core/res/res/values-ru/strings.xml10
-rw-r--r--core/res/res/values-si-watch/strings.xml6
-rw-r--r--core/res/res/values-si/strings.xml10
-rw-r--r--core/res/res/values-sk-watch/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml10
-rw-r--r--core/res/res/values-sl-watch/strings.xml6
-rw-r--r--core/res/res/values-sl/strings.xml10
-rw-r--r--core/res/res/values-sq-watch/strings.xml6
-rw-r--r--core/res/res/values-sq/strings.xml10
-rw-r--r--core/res/res/values-sr-watch/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml10
-rw-r--r--core/res/res/values-sv-watch/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml10
-rw-r--r--core/res/res/values-sw-watch/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml14
-rw-r--r--core/res/res/values-ta-watch/strings.xml6
-rw-r--r--core/res/res/values-ta/strings.xml10
-rw-r--r--core/res/res/values-te-watch/strings.xml6
-rw-r--r--core/res/res/values-te/strings.xml10
-rw-r--r--core/res/res/values-th-watch/strings.xml6
-rw-r--r--core/res/res/values-th/strings.xml12
-rw-r--r--core/res/res/values-tl-watch/strings.xml6
-rw-r--r--core/res/res/values-tl/strings.xml10
-rw-r--r--core/res/res/values-tr-watch/strings.xml6
-rw-r--r--core/res/res/values-tr/strings.xml12
-rw-r--r--core/res/res/values-uk-watch/strings.xml6
-rw-r--r--core/res/res/values-uk/strings.xml10
-rw-r--r--core/res/res/values-ur-watch/strings.xml6
-rw-r--r--core/res/res/values-ur/strings.xml10
-rw-r--r--core/res/res/values-uz-watch/strings.xml6
-rw-r--r--core/res/res/values-uz/strings.xml10
-rw-r--r--core/res/res/values-vi-watch/strings.xml6
-rw-r--r--core/res/res/values-vi/strings.xml10
-rw-r--r--core/res/res/values-zh-rCN-watch/strings.xml6
-rw-r--r--core/res/res/values-zh-rCN/strings.xml10
-rw-r--r--core/res/res/values-zh-rHK-watch/strings.xml6
-rw-r--r--core/res/res/values-zh-rHK/strings.xml11
-rw-r--r--core/res/res/values-zh-rTW-watch/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml9
-rw-r--r--core/res/res/values-zu-watch/strings.xml6
-rw-r--r--core/res/res/values-zu/strings.xml10
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/attrs_manifest.xml6
-rw-r--r--core/res/res/values/colors.xml2
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/config_telephony.xml11
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--core/res/res/values/styles.xml83
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/res/res/xml/sms_short_codes.xml7
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java5
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java211
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java52
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java75
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java6
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java10
-rw-r--r--core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java103
-rw-r--r--core/tests/coretests/src/android/service/notification/ConditionTest.java100
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java16
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java52
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java113
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java192
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java42
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java14
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java14
-rw-r--r--core/tests/utiltests/Android.bp20
-rw-r--r--core/tests/utiltests/src/android/util/DataUnitTest.java (renamed from core/tests/coretests/src/android/util/DataUnitTest.java)11
-rw-r--r--core/tests/utiltests/src/android/util/EventLogTest.java (renamed from core/tests/coretests/src/android/util/EventLogTest.java)21
-rw-r--r--core/tests/utiltests/src/android/util/LocalLogTest.java (renamed from core/tests/coretests/src/android/util/LocalLogTest.java)13
-rw-r--r--core/tests/utiltests/src/android/util/SlogTest.java47
-rw-r--r--core/tests/utiltests/src/android/util/TimeUtilsTest.java117
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--data/etc/services.core.protolog.json36
-rw-r--r--data/keyboards/Generic.kl2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java6
-rw-r--r--graphics/java/android/graphics/BaseRecordingCanvas.java4
-rw-r--r--graphics/java/android/graphics/Mesh.java3
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java3
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java48
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java9
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java3
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java3
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java4
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig15
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/config_tv.xml22
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt138
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt142
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt543
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java223
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java801
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/Android.bp4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt211
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java171
-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/transition/ShellTransitionTests.java41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java40
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/SkiaCanvas.cpp34
-rw-r--r--libs/hwui/SkiaCanvas.h2
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp3
-rw-r--r--libs/hwui/hwui/Paint.h26
-rw-r--r--libs/hwui/jni/FontFamily.cpp3
-rw-r--r--libs/hwui/jni/Paint.cpp6
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp8
-rw-r--r--libs/hwui/jni/fonts/Font.cpp3
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp122
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp3
-rw-r--r--libs/hwui/tests/unit/UnderlineTest.cpp3
-rw-r--r--libs/hwui/utils/TypefaceUtils.cpp28
-rw-r--r--libs/hwui/utils/TypefaceUtils.h28
-rw-r--r--libs/input/PointerController.cpp41
-rw-r--r--libs/input/PointerController.h70
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java2
-rw-r--r--media/java/android/media/AudioDevicePort.java2
-rw-r--r--media/java/android/media/AudioHalVersionInfo.java3
-rw-r--r--media/java/android/media/AudioManager.java29
-rw-r--r--media/java/android/media/AudioRecord.java13
-rw-r--r--media/java/android/media/IAudioService.aidl20
-rw-r--r--media/java/android/media/ILoudnessCodecUpdatesDispatcher.aidl31
-rw-r--r--media/java/android/media/LoudnessCodecConfigurator.java225
-rw-r--r--media/java/android/media/LoudnessCodecDispatcher.java109
-rw-r--r--media/java/android/media/LoudnessCodecFormat.aidl30
-rw-r--r--media/java/android/media/MediaDrm.java4
-rw-r--r--media/java/android/media/MediaMetrics.java1
-rw-r--r--media/java/android/media/MediaRouter2.java26
-rw-r--r--media/java/android/media/Spatializer.java12
-rw-r--r--media/java/android/media/Utils.java31
-rw-r--r--media/java/android/media/tv/ad/ITvAdManager.aidl25
-rw-r--r--media/java/android/media/tv/ad/ITvAdSession.aidl25
-rw-r--r--media/java/android/media/tv/ad/TvAdManager.java65
-rw-r--r--media/java/android/media/tv/ad/TvAdService.java60
-rw-r--r--media/java/android/media/tv/ad/TvAdServiceInfo.aidl19
-rw-r--r--media/java/android/media/tv/ad/TvAdServiceInfo.java194
-rw-r--r--media/java/android/media/tv/ad/TvAdView.java57
-rw-r--r--media/jni/android_media_tv_Tuner.cpp2
-rw-r--r--native/android/TEST_MAPPING10
-rw-r--r--native/android/libandroid.map.txt2
-rw-r--r--native/android/tests/thermal/Android.bp65
-rw-r--r--native/android/tests/thermal/NativeThermalUnitTest.cpp158
-rw-r--r--native/android/tests/thermal/OWNERS1
-rw-r--r--native/android/thermal.cpp124
-rw-r--r--nfc/Android.bp51
-rw-r--r--nfc/OWNERS2
-rw-r--r--nfc/TEST_MAPPING10
-rw-r--r--nfc/api/current.txt1
-rw-r--r--nfc/api/module-lib-current.txt1
-rw-r--r--nfc/api/module-lib-removed.txt1
-rw-r--r--nfc/api/removed.txt1
-rw-r--r--nfc/api/system-current.txt1
-rw-r--r--nfc/api/system-removed.txt1
-rw-r--r--nfc/api/test-current.txt1
-rw-r--r--nfc/api/test-removed.txt1
-rw-r--r--nfc/java/android/nfc/Placeholder.java27
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml2
-rw-r--r--packages/CredentialManager/res/values-it/strings.xml2
-rw-r--r--packages/CredentialManager/res/values-nb/strings.xml6
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt49
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt57
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt71
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt36
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt49
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt23
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt57
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt48
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt11
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt17
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt13
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt9
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt32
-rw-r--r--packages/InputDevices/res/values-sk/strings.xml26
-rw-r--r--packages/PackageInstaller/Android.bp9
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml23
-rw-r--r--packages/PackageInstaller/res/values/themes.xml4
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java32
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java26
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java38
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java16
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java32
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java35
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/EventResultPersister.java378
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallEventReceiver.java77
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java899
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java445
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java126
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/TemporaryFileManager.java92
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java127
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java69
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java47
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java27
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java34
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java27
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java95
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java99
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java38
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java336
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java63
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java72
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java95
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java107
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java64
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java69
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java100
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java62
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java101
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java45
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml10
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml10
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml8
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CopyablePageProvider.kt61
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml2
-rw-r--r--packages/SettingsLib/Spa/screenshot/Android.bp9
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/Android.bp74
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml22
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.pngbin0 -> 22490 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.pngbin0 -> 28743 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.pngbin0 -> 16698 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.pngbin0 -> 23550 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.pngbin0 -> 55257 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.pngbin0 -> 56190 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.pngbin0 -> 76574 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.pngbin0 -> 57028 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.pngbin0 -> 79257 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.pngbin0 -> 57340 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.pngbin0 -> 8939 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.pngbin0 -> 89649 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.pngbin0 -> 73691 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.pngbin0 -> 18788 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.pngbin0 -> 23270 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.pngbin0 -> 16698 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.pngbin0 -> 15223 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.pngbin0 -> 41062 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.pngbin0 -> 49100 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.pngbin0 -> 66160 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.pngbin0 -> 51006 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.pngbin0 -> 91931 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.pngbin0 -> 48422 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.pngbin0 -> 8939 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.pngbin0 -> 80160 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.pngbin0 -> 66109 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.pngbin0 -> 11265 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.pngbin0 -> 14438 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.pngbin0 -> 9075 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.pngbin0 -> 9828 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.pngbin0 -> 27937 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.pngbin0 -> 28749 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.pngbin0 -> 37958 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.pngbin0 -> 28404 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.pngbin0 -> 60602 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.pngbin0 -> 28871 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.pngbin0 -> 5155 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.pngbin0 -> 48080 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.pngbin0 -> 39560 bytes
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties15
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml52
-rw-r--r--packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml15
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt28
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java20
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java20
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java20
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java20
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt12
-rw-r--r--packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java20
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/CopyableBody.kt102
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt1
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt83
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt42
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt14
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt57
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt40
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt36
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt63
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt63
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt26
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreference.kt47
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt105
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt130
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt80
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt67
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt11
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt37
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt26
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt73
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreferenceTest.kt157
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_untethered_earbuds.xml29
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml39
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml35
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml24
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml24
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml33
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml45
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml45
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java1
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java4
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/Android.bp26
-rw-r--r--packages/SystemUI/AndroidManifest.xml18
-rw-r--r--packages/SystemUI/OWNERS4
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig50
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt14
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt310
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt61
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt120
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt71
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt109
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt33
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt159
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt75
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt247
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt43
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt32
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt193
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt79
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt123
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt126
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt18
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt22
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt18
-rw-r--r--packages/SystemUI/compose/scene/Android.bp9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt350
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt116
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt120
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt41
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt120
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt99
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt59
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt26
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt30
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt48
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt92
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt14
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt34
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt19
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt50
-rw-r--r--packages/SystemUI/compose/scene/tests/Android.bp5
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt457
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt113
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt76
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt23
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt2
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt56
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt3
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt1
-rwxr-xr-xpackages/SystemUI/flag_check.py8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt126
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt148
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt124
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt88
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt67
-rw-r--r--packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_assistant_attention_indicator.xml39
-rw-r--r--packages/SystemUI/res/drawable/ic_ksh_key_meta.xml15
-rw-r--r--packages/SystemUI/res/drawable/ic_person_outline.xml26
-rw-r--r--packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml19
-rw-r--r--packages/SystemUI/res/drawable/shortcut_button_colored.xml4
-rw-r--r--packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml4
-rw-r--r--packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml27
-rw-r--r--packages/SystemUI/res/layout/app_clips_screenshot.xml16
-rw-r--r--packages/SystemUI/res/layout/bluetooth_device_item.xml3
-rw-r--r--packages/SystemUI/res/layout/bluetooth_tile_dialog.xml24
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml13
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml8
-rw-r--r--packages/SystemUI/res/layout/udfps_touch_overlay.xml (renamed from packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml)25
-rw-r--r--packages/SystemUI/res/layout/widget_picker.xml26
-rw-r--r--packages/SystemUI/res/values-af/strings.xml18
-rw-r--r--packages/SystemUI/res/values-am/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml18
-rw-r--r--packages/SystemUI/res/values-as/strings.xml14
-rw-r--r--packages/SystemUI/res/values-az/strings.xml18
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-be/strings.xml18
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml18
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml18
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml18
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml14
-rw-r--r--packages/SystemUI/res/values-da/strings.xml20
-rw-r--r--packages/SystemUI/res/values-de/strings.xml20
-rw-r--r--packages/SystemUI/res/values-el/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml14
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml14
-rw-r--r--packages/SystemUI/res/values-es/strings.xml18
-rw-r--r--packages/SystemUI/res/values-et/strings.xml18
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml20
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml18
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml18
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml18
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml18
-rw-r--r--packages/SystemUI/res/values-in/strings.xml20
-rw-r--r--packages/SystemUI/res/values-is/strings.xml18
-rw-r--r--packages/SystemUI/res/values-it/strings.xml18
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml22
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml18
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-km/strings.xml14
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml18
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml18
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml18
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml22
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml18
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml14
-rw-r--r--packages/SystemUI/res/values-my/strings.xml22
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml16
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-or/strings.xml18
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml18
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml16
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml18
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml18
-rw-r--r--packages/SystemUI/res/values-si/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml18
-rw-r--r--packages/SystemUI/res/values-te/strings.xml18
-rw-r--r--packages/SystemUI/res/values-th/strings.xml14
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml18
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml14
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml18
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml18
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml16
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml16
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml18
-rw-r--r--packages/SystemUI/res/values/ids.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml19
-rw-r--r--packages/SystemUI/res/values/styles.xml17
-rw-r--r--packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButton.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java51
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java (renamed from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java)86
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt (renamed from packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt167
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt (renamed from packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt)19
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimBouncerModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimPukInputModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryModule.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt218
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt340
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt)28
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt128
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt21
-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/KeyguardSectionsModule.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt217
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/util/WallpaperPickerIntentUtils.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneComponent.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneModule.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/model/FlashlightTileModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt149
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/OWNERS8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt305
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt107
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt127
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt152
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt211
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt194
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt333
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt23
-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/ExpandableNotificationRow.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java199
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java202
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/HideListViewModel.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt)0
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/EventLog.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/EventLogImpl.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt145
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Rect.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java)46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt193
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt158
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt201
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt194
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt351
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt253
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt172
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt152
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt135
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt251
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt179
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt204
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt159
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt170
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt164
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt294
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt121
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt150
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt)367
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt415
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt583
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt129
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt217
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt893
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt302
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt313
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt107
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java159
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java321
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt161
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt (renamed from packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/InstanceIdSequenceFake.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt)10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt (renamed from packages/SystemUI/tests/src/com/android/SysUITestModule.kt)31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt (renamed from packages/SystemUI/tests/src/com/android/TestMocksModule.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeSimBouncerRepository.kt68
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/QsEventLoggerFake.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/flashlight/FlashlightTileKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt54
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt107
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt48
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt)0
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt)0
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt53
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt57
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeEventLog.kt55
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/FakeAnimationUtilDataLayerModule.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/repository/FakeAnimationStatusRepository.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java15
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java6
-rw-r--r--ravenwood/Android.bp41
-rw-r--r--ravenwood/README-ravenwood+mockito.md24
-rw-r--r--ravenwood/TEST_MAPPING7
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java)2
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java)5
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java34
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java33
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java)4
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java)2
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java)2
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java)7
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java (renamed from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java)2
-rw-r--r--ravenwood/framework-minus-apex-ravenwood-policies.txt (renamed from framework-minus-apex-ravenwood-policies.txt)53
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java29
-rw-r--r--ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java22
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java64
-rw-r--r--ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java29
-rw-r--r--ravenwood/mockito/Android.bp72
-rw-r--r--ravenwood/mockito/AndroidManifest.xml28
-rw-r--r--ravenwood/mockito/AndroidTest.xml31
-rw-r--r--ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java106
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt46
-rw-r--r--ravenwood/ravenwood-standard-options.txt20
-rw-r--r--services/Android.bp1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java136
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java33
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java57
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java311
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java (renamed from services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java)6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java6
-rw-r--r--services/backup/Android.bp13
-rw-r--r--services/backup/flags.aconfig10
-rw-r--r--services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java23
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java10
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java36
-rw-r--r--services/companion/Android.bp10
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java6
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java60
-rw-r--r--services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java147
-rw-r--r--services/companion/java/com/android/server/companion/virtual/OWNERS2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java92
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java153
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl39
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java258
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java100
-rw-r--r--services/core/Android.bp4
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/PinnerService.java45
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java21
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING12
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java87
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java5
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java276
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java8
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java41
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java10
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java7
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java27
-rw-r--r--services/core/java/com/android/server/am/OomAdjusterModernImpl.java144
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java23
-rw-r--r--services/core/java/com/android/server/am/ProcessProfileRecord.java113
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java48
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java24
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java20
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java15
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java13
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java22
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java193
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java24
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java6
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java23
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java114
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java45
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java30
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java19
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java7
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java38
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java545
-rw-r--r--services/core/java/com/android/server/content/ContentService.java3
-rw-r--r--services/core/java/com/android/server/content/SyncJobService.java16
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java47
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java33
-rw-r--r--services/core/java/com/android/server/display/DisplayBrightnessState.java41
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java122
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java154
-rw-r--r--services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java91
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java4
-rw-r--r--services/core/java/com/android/server/display/ExternalDisplayPolicy.java298
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java34
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java7
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java9
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java65
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessReason.java5
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java23
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java30
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java1
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java74
-rw-r--r--services/core/java/com/android/server/display/config/SensorData.java184
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java5
-rw-r--r--services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java31
-rw-r--r--services/core/java/com/android/server/display/state/DisplayStateController.java5
-rw-r--r--services/core/java/com/android/server/display/utils/SensorUtils.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java3
-rw-r--r--services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java66
-rw-r--r--services/core/java/com/android/server/input/InputSettingsObserver.java56
-rw-r--r--services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java4
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java23
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java30
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java20
-rw-r--r--services/core/java/com/android/server/inputmethod/SubtypeUtils.java3
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java153
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java13
-rw-r--r--services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java2
-rw-r--r--services/core/java/com/android/server/media/DeviceRouteController.java8
-rw-r--r--services/core/java/com/android/server/media/LegacyDeviceRouteController.java2
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java249
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java16
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkManagementService.java7
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java132
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java8
-rw-r--r--services/core/java/com/android/server/notification/ZenAdapters.java78
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java273
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig2
-rw-r--r--services/core/java/com/android/server/pdb/PersistentDataBlockService.java29
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java9
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java6
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterUtils.java8
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java16
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java13
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java47
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java113
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java75
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerInternalBase.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageProperty.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java24
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java10
-rw-r--r--services/core/java/com/android/server/pm/Settings.java43
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java2
-rw-r--r--services/core/java/com/android/server/pm/UpdateOwnershipHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/UserJourneyLogger.java4
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java22
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java63
-rw-r--r--services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java8
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java43
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionRegistry.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackage.java29
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageState.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserState.java6
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java26
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedUserApi.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java7
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java6
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java24
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java35
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolver.java18
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java6
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java10
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java6
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java4
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java2
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java62
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java76
-rw-r--r--services/core/java/com/android/server/power/OWNERS4
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java85
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java18
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java71
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java4
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java96
-rw-r--r--services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java9
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java7
-rw-r--r--services/core/java/com/android/server/timedetector/TEST_MAPPING5
-rw-r--r--services/core/java/com/android/server/timezonedetector/TEST_MAPPING6
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java18
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java101
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java40
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java2
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java83
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java60
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java18
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java33
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java13
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java133
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java197
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java38
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java58
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java33
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java14
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java60
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java11
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java18
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java14
-rw-r--r--services/core/java/com/android/server/wm/InputTarget.java4
-rw-r--r--services/core/java/com/android/server/wm/InputWindowHandleWrapper.java5
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java12
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java7
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java16
-rw-r--r--services/core/java/com/android/server/wm/Session.java10
-rw-r--r--services/core/java/com/android/server/wm/Task.java77
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java25
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerFlags.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java36
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java150
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java44
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp19
-rw-r--r--services/core/jni/tvinput/JTvInputHal.cpp19
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd14
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java20
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt24
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt46
-rw-r--r--services/proguard.flags13
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml2
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java78
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java4
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java20
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java8
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt3
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt4
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt4
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt6
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt15
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt4
-rw-r--r--services/tests/displayservicetests/Android.bp11
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java44
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java22
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java91
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java125
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java79
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java41
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt51
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java283
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java22
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java20
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java129
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java23
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java64
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java14
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java6
-rw-r--r--services/tests/mockingservicestests/Android.bp1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java259
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java283
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java217
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java134
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java60
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java341
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java200
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/OWNERS2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java6
-rw-r--r--services/tests/servicestests/res/xml/usertypes_test_profile.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/OWNERS5
-rw-r--r--services/tests/servicestests/src/com/android/server/PinnerServiceTest.java144
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java93
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java)10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java224
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java278
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java222
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java157
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/job/JobStoreTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java96
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt68
-rw-r--r--services/tests/servicestests/src/com/android/server/power/OWNERS2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java82
-rw-r--r--services/tests/timetests/TEST_MAPPING3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java21
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java314
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java98
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java9
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java130
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java119
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java16
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java70
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java569
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java97
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java6
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java78
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java44
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java44
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java92
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java2
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java23
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java20
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java44
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java24
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java2
-rw-r--r--telecomm/java/android/telecom/CallAttributes.java5
-rw-r--r--telephony/OWNERS4
-rw-r--r--telephony/java/android/service/euicc/EuiccProfileInfo.java18
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java54
-rw-r--r--telephony/java/android/telephony/NumberVerificationCallback.java4
-rw-r--r--telephony/java/android/telephony/PinResult.java3
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java118
-rw-r--r--telephony/java/android/telephony/SmsManager.java14
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java4
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java6
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java52
-rw-r--r--telephony/java/android/telephony/data/DataService.java81
-rw-r--r--telephony/java/android/telephony/data/IDataService.aidl3
-rw-r--r--telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl3
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java97
-rw-r--r--telephony/java/android/telephony/data/ThrottleStatus.java4
-rw-r--r--telephony/java/android/telephony/euicc/EuiccCardManager.java12
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java3
-rw-r--r--telephony/java/android/telephony/euicc/EuiccNotification.java6
-rw-r--r--telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java6
-rw-r--r--telephony/java/android/telephony/ims/MediaQualityStatus.java3
-rw-r--r--telephony/java/android/telephony/ims/RcsClientConfiguration.java3
-rw-r--r--telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl33
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java39
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java80
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl9
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl36
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java6
-rw-r--r--tests/CtsSurfaceControlTestsStaging/OWNERS2
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java47
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt17
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt15
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt19
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt18
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt96
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt18
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt4
-rw-r--r--tests/MotionPrediction/Android.bp3
-rw-r--r--tests/MultiDeviceInput/Android.bp33
-rw-r--r--tests/MultiDeviceInput/AndroidManifest.xml34
-rw-r--r--tests/MultiDeviceInput/OWNERS1
-rw-r--r--tests/MultiDeviceInput/README.md19
-rw-r--r--tests/MultiDeviceInput/res/layout/activity_main.xml27
-rw-r--r--tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 10486 bytes
-rw-r--r--tests/MultiDeviceInput/res/values-w820dp/dimens.xml20
-rw-r--r--tests/MultiDeviceInput/res/values/colors.xml20
-rw-r--r--tests/MultiDeviceInput/res/values/dimens.xml19
-rw-r--r--tests/MultiDeviceInput/res/values/strings.xml17
-rw-r--r--tests/MultiDeviceInput/res/values/styles.xml23
-rw-r--r--tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt183
-rw-r--r--tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt83
-rw-r--r--tests/SmokeTestApps/Android.bp3
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java10
-rw-r--r--tools/aapt2/Android.bp1
-rw-r--r--tools/aapt2/cmd/Link.cpp22
-rw-r--r--tools/aapt2/cmd/Link.h7
-rw-r--r--tools/aapt2/cmd/Util.cpp50
-rw-r--r--tools/aapt2/cmd/Util.h14
-rw-r--r--tools/aapt2/cmd/Util_test.cpp46
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter.cpp104
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter.h79
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter_test.cpp236
-rw-r--r--tools/hoststubgen/TEST_MAPPING7
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp3
-rw-r--r--tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java)2
-rw-r--r--tools/hoststubgen/hoststubgen/framework-policy-override.txt3
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java69
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java6
-rw-r--r--tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java2
-rw-r--r--tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt4
-rwxr-xr-xtools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh32
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt2
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt7
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt20
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt64
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt21
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt28
-rwxr-xr-xtools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh2
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt381
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt303
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt391
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt303
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt492
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java5
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java77
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java12
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java6
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java28
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java3
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java47
-rwxr-xr-xtools/hoststubgen/scripts/run-all-tests.sh2
-rw-r--r--tools/lint/README.md13
-rw-r--r--tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt32
-rw-r--r--tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt2
-rw-r--r--tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt87
-rw-r--r--tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt134
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt7
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt23
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java10
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java20
2200 files changed, 60270 insertions, 18501 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 44f3d706a0d0..ce3e985e22d5 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -36,6 +36,7 @@ aconfig_srcjars = [
":com.android.hardware.input-aconfig-java{.generated_srcjars}",
":com.android.input.flags-aconfig-java{.generated_srcjars}",
":com.android.text.flags-aconfig-java{.generated_srcjars}",
+ ":framework-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
":telecom_flags_core_java_lib{.generated_srcjars}",
":telephony_flags_core_java_lib{.generated_srcjars}",
":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
@@ -58,11 +59,11 @@ aconfig_srcjars = [
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
":device_policy_aconfig_flags_lib{.generated_srcjars}",
- ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
":surfaceflinger_flags_java_lib{.generated_srcjars}",
":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
":android.tracing.flags-aconfig-java{.generated_srcjars}",
+ ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -108,6 +109,11 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "telephony_flags_c_lib",
+ aconfig_declarations: "telephony_flags",
+}
+
// Window
aconfig_declarations {
name: "com.android.window.flags.window-aconfig",
@@ -180,6 +186,18 @@ aconfig_declarations {
srcs: ["core/java/android/nfc/*.aconfig"],
}
+cc_aconfig_library {
+ name: "android_nfc_flags_aconfig_c_lib",
+ vendor_available: true,
+ aconfig_declarations: "android.nfc.flags-aconfig",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ "nfc_nci.st21nfc.default",
+ ],
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
@@ -208,6 +226,7 @@ java_aconfig_library {
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
+ mode: "test",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -237,6 +256,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.os.flags-aconfig-java-host",
+ aconfig_declarations: "android.os.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// VirtualDeviceManager
java_aconfig_library {
name: "android.companion.virtual.flags-aconfig-java",
@@ -639,6 +665,19 @@ cc_aconfig_library {
aconfig_declarations: "device_policy_aconfig_flags",
}
+// JobScheduler
+aconfig_declarations {
+ name: "framework-jobscheduler-job.flags-aconfig",
+ package: "android.app.job",
+ srcs: ["apex/jobscheduler/framework/aconfig/job.aconfig"],
+}
+
+java_aconfig_library {
+ name: "framework-jobscheduler-job.flags-aconfig-java",
+ aconfig_declarations: "framework-jobscheduler-job.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Notifications
aconfig_declarations {
name: "android.service.notification.flags-aconfig",
@@ -710,3 +749,16 @@ java_aconfig_library {
aconfig_declarations: "android.tracing.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// App Widgets
+aconfig_declarations {
+ name: "android.appwidget.flags-aconfig",
+ package: "android.appwidget.flags",
+ srcs: ["core/java/android/appwidget/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.appwidget.flags-aconfig-java",
+ aconfig_declarations: "android.appwidget.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 895ef98c5537..78ffd6fb5e69 100644
--- a/Android.bp
+++ b/Android.bp
@@ -249,6 +249,7 @@ java_library {
"android.se.omapi-V1-java",
"android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
+ "ImmutabilityAnnotation",
"com.android.sysprop.init",
"com.android.sysprop.localization",
@@ -413,13 +414,25 @@ java_defaults {
],
}
+// Collection of non updatable unbundled jars. The list here should match
+// |non_updatable_modules| variable in frameworks/base/api/api.go.
+java_library {
+ name: "framework-non-updatable-unbundled-impl-libs",
+ static_libs: [
+ "framework-location.impl",
+ "framework-nfc.impl",
+ ],
+ sdk_version: "core_platform",
+ installable: false,
+}
+
// Separated so framework-minus-apex-defaults can be used without the libs dependency
java_defaults {
name: "framework-minus-apex-with-libs-defaults",
defaults: ["framework-minus-apex-defaults"],
libs: [
"framework-virtualization.stubs.module_lib",
- "framework-location.impl",
+ "framework-non-updatable-unbundled-impl-libs",
],
}
@@ -450,7 +463,7 @@ java_library {
stem: "framework",
apex_available: ["//apex_available:platform"],
visibility: [
- "//frameworks/base/location",
+ "//frameworks/base:__subpackages__",
],
compile_dex: false,
headers_only: true,
@@ -513,8 +526,8 @@ java_library {
installable: false, // this lib is a build-only library
static_libs: [
"app-compat-annotations",
- "framework-location.impl",
"framework-minus-apex",
+ "framework-non-updatable-unbundled-impl-libs",
"framework-updatable-stubs-module_libs_api",
],
sdk_version: "core_platform",
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index e02ba770cdf8..44b2f3805495 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -5,3 +5,5 @@ michaelwr@google.com
prabirmsp@google.com
svv@google.com
vdevmurari@google.com
+
+per-file Virtual*=file:/services/companion/java/com/android/server/companion/virtual/OWNERS
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 3310898ccfb5..03a23ba15273 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -30,7 +30,7 @@ java_genrule {
name: "framework-minus-apex.ravenwood-base",
tools: ["hoststubgen"],
cmd: "$(location hoststubgen) " +
- "@$(location :ravenwood-standard-options) " +
+ "@$(location ravenwood/ravenwood-standard-options.txt) " +
"--out-stub-jar $(location ravenwood_stub.jar) " +
"--out-impl-jar $(location ravenwood.jar) " +
@@ -39,11 +39,13 @@ java_genrule {
"--gen-input-dump-file $(location hoststubgen_dump.txt) " +
"--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
- "--policy-override-file $(location framework-minus-apex-ravenwood-policies.txt) ",
+ "--policy-override-file $(location ravenwood/framework-minus-apex-ravenwood-policies.txt) " +
+ "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ",
srcs: [
":framework-minus-apex-for-hoststubgen",
- "framework-minus-apex-ravenwood-policies.txt",
- ":ravenwood-standard-options",
+ "ravenwood/framework-minus-apex-ravenwood-policies.txt",
+ "ravenwood/ravenwood-standard-options.txt",
+ "ravenwood/ravenwood-annotation-allowed-classes.txt",
],
out: [
"ravenwood.jar",
@@ -79,7 +81,8 @@ android_ravenwood_libgroup {
"hoststubgen-helper-framework-runtime.ravenwood",
"junit",
"truth",
- "ravenwood-junit",
+ "ravenwood-junit-impl",
+ "android.test.mock",
],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b215ee4e2537..3409838b5611 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -128,6 +128,17 @@
]
}
],
+ "postsubmit-ravenwood": [
+ // TODO(ravenwood) promote it to presubmit
+ // TODO: Enable it once the infra knows how to run it.
+// {
+// "name": "CtsUtilTestCasesRavenwood",
+// "file_patterns": [
+// "*Ravenwood*",
+// "*ravenwood*"
+// ]
+// }
+ ],
"postsubmit-managedprofile-stress": [
{
"name": "ManagedProfileLifecycleStressTest"
diff --git a/THERMAL_OWNERS b/THERMAL_OWNERS
new file mode 100644
index 000000000000..b95b7e84191c
--- /dev/null
+++ b/THERMAL_OWNERS
@@ -0,0 +1,3 @@
+lpy@google.com
+wvw@google.com
+xwxw@google.com
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
index 4c57e640c141..8ff3f9bc6620 100644
--- a/apct-tests/perftests/OWNERS
+++ b/apct-tests/perftests/OWNERS
@@ -1,12 +1,11 @@
-balejs@google.com
carmenjackson@google.com
-cfijalkovich@google.com
dualli@google.com
edgararriaga@google.com
-jpakaravoor@google.com
+jdduke@google.com
jreck@google.com #{LAST_RESORT_SUGGESTION}
kevinjeon@google.com
philipcuadra@google.com
+shayba@google.com
shombert@google.com
timmurray@google.com
wessam@google.com
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 9366ff2d81a9..e1b3241e051e 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -66,6 +66,7 @@ android_test {
errorprone: {
javacflags: [
"-Xep:ReturnValueIgnored:WARN",
+ "-Xep:UnnecessaryStringBuilder:OFF",
],
},
}
diff --git a/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
new file mode 100644
index 000000000000..d8b2d759e4c0
--- /dev/null
+++ b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
Binary files differ
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index f84a0d037ca5..e5a06c9bd146 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,20 +16,29 @@
package android.graphics.perftests;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
+import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import com.android.perftests.core.R;
+
import org.junit.Rule;
import org.junit.Test;
+import java.io.IOException;
+
@LargeTest
public class CanvasPerfTest {
@Rule
@@ -93,4 +102,38 @@ public class CanvasPerfTest {
node.end(canvas);
}
}
+
+ @Test
+ public void testCreateScaledBitmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ source.setGainmap(null);
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
+
+ @Test
+ public void testCreateScaledBitmapWithGainmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ assertTrue(source.hasGainmap());
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index b87e42e31da3..72816e4ce900 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -112,7 +112,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
state.addExtraResult("add", elapsedTimeNsOfAdd);
startTime = SystemClock.elapsedRealtimeNanos();
- session.remove(this);
+ session.remove(asBinder());
final long elapsedTimeNsOfRemove = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("remove", elapsedTimeNsOfRemove);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 849432653bb3..4a0d57f55086 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -536,7 +536,7 @@ class BlobMetadata {
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
String callingPackage, int callingUid) throws IOException {
final RevocableFileDescriptor revocableFd =
- new RevocableFileDescriptor(mContext, fd);
+ new RevocableFileDescriptor(mContext, fd, BlobStoreUtils.getRevocableFdHandler());
final Accessor accessor;
synchronized (mRevocableFds) {
accessor = new Accessor(callingPackage, callingUid);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 8eef8cebec3f..ede29ec168c0 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -223,7 +223,8 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
FileDescriptor fd = null;
try {
fd = openWriteInternal(offsetBytes, lengthBytes);
- final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd,
+ BlobStoreUtils.getRevocableFdHandler());
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
IoUtils.closeQuietly(fd);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 8f9427303dff..a4eae014bacd 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,8 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UserHandle;
import android.text.format.TimeMigrationUtils;
import android.util.Slog;
@@ -63,4 +65,27 @@ class BlobStoreUtils {
static String formatTime(long timeMs) {
return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs);
}
+
+ private static Handler sRevocableFdHandler;
+ private static final Object sLock = new Object();
+
+ // By default, when using a RevocableFileDescriptor, callbacks will be sent to the process'
+ // main looper. In this case that would be system_server's main looper, which is a heavily
+ // contended thread. It can also cause deadlocks, because the volume daemon 'vold' holds a lock
+ // while making these callbacks to the system_server, while at the same time the system_server
+ // main thread can make a call into vold, which requires that same vold lock. To avoid these
+ // issues, use a separate thread for the RevocableFileDescriptor's requests, so that it can
+ // make progress independently of system_server.
+ static @NonNull Handler getRevocableFdHandler() {
+ synchronized (sLock) {
+ if (sRevocableFdHandler != null) {
+ return sRevocableFdHandler;
+ }
+ final HandlerThread t = new HandlerThread("BlobFuseLooper");
+ t.start();
+ sRevocableFdHandler = new Handler(t.getLooper());
+
+ return sRevocableFdHandler;
+ }
+ }
}
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
new file mode 100644
index 000000000000..f5e33a80211b
--- /dev/null
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -0,0 +1,8 @@
+package: "android.app.job"
+
+flag {
+ name: "job_debug_info_apis"
+ namespace: "backstage_power"
+ description: "Add APIs to let apps attach debug information to jobs"
+ bug: "293491637"
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 9961c4fdf3f7..742ed5f2eeb7 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.util.TimeUtils.formatDuration;
import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,13 +48,17 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.os.Trace;
+import android.util.ArraySet;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
/**
* Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
@@ -423,6 +428,15 @@ public class JobInfo implements Parcelable {
*/
public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3;
+ /** @hide */
+ public static final int MAX_NUM_DEBUG_TAGS = 32;
+
+ /** @hide */
+ public static final int MAX_DEBUG_TAG_LENGTH = 127;
+
+ /** @hide */
+ public static final int MAX_TRACE_TAG_LENGTH = Trace.MAX_SECTION_NAME_LEN;
+
@UnsupportedAppUsage
private final int jobId;
private final PersistableBundle extras;
@@ -454,6 +468,9 @@ public class JobInfo implements Parcelable {
private final int mPriority;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int flags;
+ private final ArraySet<String> mDebugTags;
+ @Nullable
+ private final String mTraceTag;
/**
* Unique job id associated with this application (uid). This is the same job ID
@@ -724,6 +741,33 @@ public class JobInfo implements Parcelable {
}
/**
+ * @see JobInfo.Builder#addDebugTag(String)
+ */
+ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @NonNull
+ public Set<String> getDebugTags() {
+ return Collections.unmodifiableSet(mDebugTags);
+ }
+
+ /**
+ * @see JobInfo.Builder#addDebugTag(String)
+ * @hide
+ */
+ @NonNull
+ public ArraySet<String> getDebugTagsArraySet() {
+ return mDebugTags;
+ }
+
+ /**
+ * @see JobInfo.Builder#setTraceTag(String)
+ */
+ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @Nullable
+ public String getTraceTag() {
+ return mTraceTag;
+ }
+
+ /**
* @see JobInfo.Builder#setExpedited(boolean)
*/
public boolean isExpedited() {
@@ -860,6 +904,12 @@ public class JobInfo implements Parcelable {
if (flags != j.flags) {
return false;
}
+ if (!mDebugTags.equals(j.mDebugTags)) {
+ return false;
+ }
+ if (!Objects.equals(mTraceTag, j.mTraceTag)) {
+ return false;
+ }
return true;
}
@@ -904,6 +954,12 @@ public class JobInfo implements Parcelable {
hashCode = 31 * hashCode + mBias;
hashCode = 31 * hashCode + mPriority;
hashCode = 31 * hashCode + flags;
+ if (mDebugTags.size() > 0) {
+ hashCode = 31 * hashCode + mDebugTags.hashCode();
+ }
+ if (mTraceTag != null) {
+ hashCode = 31 * hashCode + mTraceTag.hashCode();
+ }
return hashCode;
}
@@ -946,6 +1002,17 @@ public class JobInfo implements Parcelable {
mBias = in.readInt();
mPriority = in.readInt();
flags = in.readInt();
+ final int numDebugTags = in.readInt();
+ mDebugTags = new ArraySet<>();
+ for (int i = 0; i < numDebugTags; ++i) {
+ final String tag = in.readString();
+ if (tag == null) {
+ throw new IllegalStateException("malformed parcel");
+ }
+ mDebugTags.add(tag.intern());
+ }
+ final String traceTag = in.readString();
+ mTraceTag = traceTag == null ? null : traceTag.intern();
}
private JobInfo(JobInfo.Builder b) {
@@ -978,6 +1045,8 @@ public class JobInfo implements Parcelable {
mBias = b.mBias;
mPriority = b.mPriority;
flags = b.mFlags;
+ mDebugTags = b.mDebugTags;
+ mTraceTag = b.mTraceTag;
}
@Override
@@ -1024,6 +1093,14 @@ public class JobInfo implements Parcelable {
out.writeInt(mBias);
out.writeInt(mPriority);
out.writeInt(this.flags);
+ // Explicitly write out values here to avoid double looping to intern the strings
+ // when unparcelling.
+ final int numDebugTags = mDebugTags.size();
+ out.writeInt(numDebugTags);
+ for (int i = 0; i < numDebugTags; ++i) {
+ out.writeString(mDebugTags.valueAt(i));
+ }
+ out.writeString(mTraceTag);
}
public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
@@ -1168,6 +1245,8 @@ public class JobInfo implements Parcelable {
private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
/** Easy way to track whether the client has tried to set a back-off policy. */
private boolean mBackoffPolicySet = false;
+ private final ArraySet<String> mDebugTags = new ArraySet<>();
+ private String mTraceTag;
/**
* Initialize a new Builder to construct a {@link JobInfo}.
@@ -1222,6 +1301,51 @@ public class JobInfo implements Parcelable {
mPriority = job.getPriority();
}
+ /**
+ * Add a debug tag to help track what this job is for. The tags may show in debug dumps
+ * or app metrics. Do not put personally identifiable information (PII) in the tag.
+ * <p>
+ * Tags have the following requirements:
+ * <ul>
+ * <li>Tags cannot be more than 127 characters.</li>
+ * <li>
+ * Since leading and trailing whitespace can lead to hard-to-debug issues,
+ * tags should not include leading or trailing whitespace.
+ * All tags will be {@link String#trim() trimmed}.
+ * </li>
+ * <li>An empty String (after trimming) is not allowed.</li>
+ * <li>Should not have personally identifiable information (PII).</li>
+ * <li>A job cannot have more than 32 tags.</li>
+ * </ul>
+ *
+ * @param tag A debug tag that helps describe what the job is for.
+ * @return This object for method chaining
+ */
+ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @NonNull
+ public Builder addDebugTag(@NonNull String tag) {
+ mDebugTags.add(validateDebugTag(tag));
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ public void addDebugTags(@NonNull Set<String> tags) {
+ mDebugTags.addAll(tags);
+ }
+
+ /**
+ * Remove a tag set via {@link #addDebugTag(String)}.
+ * @param tag The tag to remove
+ * @return This object for method chaining
+ */
+ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @NonNull
+ public Builder removeDebugTag(@NonNull String tag) {
+ mDebugTags.remove(tag);
+ return this;
+ }
+
/** @hide */
@NonNull
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
@@ -1997,6 +2121,24 @@ public class JobInfo implements Parcelable {
}
/**
+ * Set a tag that will be used in {@link android.os.Trace traces}.
+ * Since this is a trace tag, it must follow the rules set in
+ * {@link android.os.Trace#beginSection(String)}, such as it cannot be more
+ * than 127 Unicode code units.
+ * Additionally, since leading and trailing whitespace can lead to hard-to-debug issues,
+ * they will be {@link String#trim() trimmed}.
+ * An empty String (after trimming) is not allowed.
+ * @param traceTag The tag to use in traces.
+ * @return This object for method chaining
+ */
+ @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @NonNull
+ public Builder setTraceTag(@Nullable String traceTag) {
+ mTraceTag = validateTraceTag(traceTag);
+ return this;
+ }
+
+ /**
* @return The job object to hand to the JobScheduler. This object is immutable.
*/
public JobInfo build() {
@@ -2209,6 +2351,62 @@ public class JobInfo implements Parcelable {
"A user-initiated data transfer job must specify a valid network type");
}
}
+
+ if (mDebugTags.size() > MAX_NUM_DEBUG_TAGS) {
+ throw new IllegalArgumentException(
+ "Can't have more than " + MAX_NUM_DEBUG_TAGS + " tags");
+ }
+ final ArraySet<String> validatedDebugTags = new ArraySet<>();
+ for (int i = 0; i < mDebugTags.size(); ++i) {
+ validatedDebugTags.add(validateDebugTag(mDebugTags.valueAt(i)));
+ }
+ mDebugTags.clear();
+ mDebugTags.addAll(validatedDebugTags);
+
+ validateTraceTag(mTraceTag);
+ }
+
+ /**
+ * Returns a sanitized debug tag if valid, or throws an exception if not.
+ * @hide
+ */
+ @NonNull
+ public static String validateDebugTag(@Nullable String debugTag) {
+ if (debugTag == null) {
+ throw new NullPointerException("debug tag cannot be null");
+ }
+ debugTag = debugTag.trim();
+ if (debugTag.isEmpty()) {
+ throw new IllegalArgumentException("debug tag cannot be empty");
+ }
+ if (debugTag.length() > MAX_DEBUG_TAG_LENGTH) {
+ throw new IllegalArgumentException(
+ "debug tag cannot be more than " + MAX_DEBUG_TAG_LENGTH + " characters");
+ }
+ return debugTag.intern();
+ }
+
+ /**
+ * Returns a sanitized trace tag if valid, or throws an exception if not.
+ * @hide
+ */
+ @Nullable
+ public static String validateTraceTag(@Nullable String traceTag) {
+ if (traceTag == null) {
+ return null;
+ }
+ traceTag = traceTag.trim();
+ if (traceTag.isEmpty()) {
+ throw new IllegalArgumentException("trace tag cannot be empty");
+ }
+ if (traceTag.length() > MAX_TRACE_TAG_LENGTH) {
+ throw new IllegalArgumentException(
+ "traceTag tag cannot be more than " + MAX_TRACE_TAG_LENGTH + " characters");
+ }
+ if (traceTag.contains("|") || traceTag.contains("\n") || traceTag.contains("\0")) {
+ throw new IllegalArgumentException("Trace tag cannot contain |, \\n, or \\0");
+ }
+ return traceTag.intern();
}
/**
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 6c83add3b8e3..8b55e071e715 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,10 +13,6 @@ java_library {
name: "service-jobscheduler",
installable: true,
- defaults: [
- "service-jobscheduler-aconfig-libraries",
- ],
-
srcs: [
"java/**/*.java",
":framework-jobscheduler-shared-srcs",
@@ -32,6 +28,7 @@ java_library {
static_libs: [
"modules-utils-fastxmlserializer",
+ "service-jobscheduler-job.flags-aconfig-java",
],
// Rename classes shared with the framework
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 7d8a363ba819..3ba7aa201d27 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -10,7 +10,6 @@ aconfig_declarations {
java_aconfig_library {
name: "service-jobscheduler-deviceidle.flags-aconfig-java",
aconfig_declarations: "service-deviceidle.flags-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
visibility: ["//frameworks/base:__subpackages__"],
}
@@ -26,21 +25,5 @@ aconfig_declarations {
java_aconfig_library {
name: "service-jobscheduler-job.flags-aconfig-java",
aconfig_declarations: "service-job.flags-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
- visibility: ["//frameworks/base:__subpackages__"],
-}
-
-service_jobscheduler_aconfig_srcjars = [
- ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
- ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
-]
-
-// Aconfig declarations and libraries for the core framework
-java_defaults {
- name: "service-jobscheduler-aconfig-libraries",
- // Add java_aconfig_libraries to here to add them to the core framework
- srcs: service_jobscheduler_aconfig_srcjars,
- // Add aconfig-annotations-lib as a dependency for the optimization
- libs: ["aconfig-annotations-lib"],
visibility: ["//frameworks/base:__subpackages__"],
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index a720bafee1d3..6f8014faf91a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -22,7 +22,7 @@ import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static com.android.server.alarm.AlarmManagerService.PRIORITY_NORMAL;
-import static com.android.server.alarm.AlarmManagerService.clampPositive;
+import static com.android.server.alarm.AlarmManagerService.addClampPositive;
import android.app.AlarmManager;
import android.app.IAlarmListener;
@@ -148,7 +148,7 @@ class Alarm {
mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed;
mWhenElapsed = requestedWhenElapsed;
this.windowLength = windowLength;
- mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength);
+ mMaxWhenElapsed = addClampPositive(requestedWhenElapsed, windowLength);
repeatInterval = interval;
operation = op;
listener = rec;
@@ -244,8 +244,8 @@ class Alarm {
final long oldMaxWhenElapsed = mMaxWhenElapsed;
// windowLength should always be >= 0 here.
- final long maxRequestedElapsed = clampPositive(
- mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength);
+ final long maxRequestedElapsed = addClampPositive(
+ mPolicyWhenElapsed[REQUESTER_POLICY_INDEX], windowLength);
mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed);
return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed);
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 384a480af4e9..1bd8da8256c3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -1417,15 +1417,15 @@ public class AlarmManagerService extends SystemService {
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- long maxElapsed = triggerAtTime + (long) (0.75 * futurity);
+ long maxElapsed = addClampPositive(triggerAtTime, (long) (0.75 * futurity));
// For non-repeating alarms, window is capped at a maximum of one hour from the requested
// delivery time. This allows for inexact-while-idle alarms to be slightly more reliable.
// In practice, the delivery window should generally be much smaller than that
// when the device is not idling.
if (interval == 0) {
- maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR);
+ maxElapsed = Math.min(maxElapsed, addClampPositive(triggerAtTime, INTERVAL_HOUR));
}
- return clampPositive(maxElapsed);
+ return maxElapsed;
}
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
@@ -1512,6 +1512,18 @@ public class AlarmManagerService extends SystemService {
return (val >= 0) ? val : Long.MAX_VALUE;
}
+ static long addClampPositive(long val1, long val2) {
+ long val = val1 + val2;
+ if (val >= 0) {
+ return val;
+ } else if (val1 >= 0 && val2 >= 0) {
+ /* Both are +ve, so overflow happened. */
+ return Long.MAX_VALUE;
+ } else {
+ return 0;
+ }
+ }
+
/**
* Sends alarms that were blocked due to user applied background restrictions - either because
* the user lifted those or the uid came to foreground.
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 bff43534ce05..07475b4f2136 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -998,6 +998,7 @@ public class JobSchedulerService extends com.android.server.SystemService
DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO);
}
+ // TODO(141645789): move into ConnectivityController.CcConfig
private void updateConnectivityConstantsLocked() {
CONN_CONGESTION_DELAY_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_CONN_CONGESTION_DELAY_FRAC,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 721a8bdce57a..6449edcd3103 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -557,6 +557,11 @@ public final class JobServiceContext implements ServiceConnection {
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
traceTag, getId());
}
+ if (job.getAppTraceTag() != null) {
+ // Use the job's ID to distinguish traces since the ID will be unique per app.
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler",
+ job.getAppTraceTag(), job.getJobId());
+ }
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
@@ -1616,6 +1621,10 @@ public final class JobServiceContext implements ServiceConnection {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
}
+ if (completedJob.getAppTraceTag() != null) {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler",
+ completedJob.getJobId());
+ }
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
loggingInternalStopReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index d466f0d8912c..afcbddad611e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -510,6 +510,8 @@ public final class JobStore {
private static final String XML_TAG_ONEOFF = "one-off";
private static final String XML_TAG_EXTRAS = "extras";
private static final String XML_TAG_JOB_WORK_ITEM = "job-work-item";
+ private static final String XML_TAG_DEBUG_INFO = "debug-info";
+ private static final String XML_TAG_DEBUG_TAG = "debug-tag";
private void migrateJobFilesAsync() {
synchronized (mLock) {
@@ -805,6 +807,7 @@ public final class JobStore {
writeExecutionCriteriaToXml(out, jobStatus);
writeBundleToXml(jobStatus.getJob().getExtras(), out);
writeJobWorkItemsToXml(out, jobStatus);
+ writeDebugInfoToXml(out, jobStatus);
out.endTag(null, XML_TAG_JOB);
numJobs++;
@@ -991,6 +994,26 @@ public final class JobStore {
}
}
+ private void writeDebugInfoToXml(@NonNull TypedXmlSerializer out,
+ @NonNull JobStatus jobStatus) throws IOException, XmlPullParserException {
+ final ArraySet<String> debugTags = jobStatus.getJob().getDebugTagsArraySet();
+ final int numTags = debugTags.size();
+ final String traceTag = jobStatus.getJob().getTraceTag();
+ if (traceTag == null && numTags == 0) {
+ return;
+ }
+ out.startTag(null, XML_TAG_DEBUG_INFO);
+ if (traceTag != null) {
+ out.attribute(null, "trace-tag", traceTag);
+ }
+ for (int i = 0; i < numTags; ++i) {
+ out.startTag(null, XML_TAG_DEBUG_TAG);
+ out.attribute(null, "tag", debugTags.valueAt(i));
+ out.endTag(null, XML_TAG_DEBUG_TAG);
+ }
+ out.endTag(null, XML_TAG_DEBUG_INFO);
+ }
+
private void writeJobWorkItemsToXml(@NonNull TypedXmlSerializer out,
@NonNull JobStatus jobStatus) throws IOException, XmlPullParserException {
// Write executing first since they're technically at the front of the queue.
@@ -1449,6 +1472,18 @@ public final class JobStore {
jobWorkItems = readJobWorkItemsFromXml(parser);
}
+ if (eventType == XmlPullParser.START_TAG
+ && XML_TAG_DEBUG_INFO.equals(parser.getName())) {
+ try {
+ jobBuilder.setTraceTag(parser.getAttributeValue(null, "trace-tag"));
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Invalid trace tag persisted to disk", e);
+ }
+ parser.next();
+ jobBuilder.addDebugTags(readDebugTagsFromXml(parser));
+ eventType = parser.nextTag(); // Consume </debug-info>
+ }
+
final JobInfo builtJob;
try {
// Don't perform prefetch-deadline check here. Apps targeting S- shouldn't have
@@ -1721,6 +1756,33 @@ public final class JobStore {
return null;
}
}
+
+ @NonNull
+ private Set<String> readDebugTagsFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ Set<String> debugTags = new ArraySet<>();
+
+ for (int eventType = parser.getEventType(); eventType != XmlPullParser.END_DOCUMENT;
+ eventType = parser.next()) {
+ final String tagName = parser.getName();
+ if (!XML_TAG_DEBUG_TAG.equals(tagName)) {
+ // We're no longer operating with debug tags.
+ break;
+ }
+ if (debugTags.size() < JobInfo.MAX_NUM_DEBUG_TAGS) {
+ final String debugTag;
+ try {
+ debugTag = JobInfo.validateDebugTag(parser.getAttributeValue(null, "tag"));
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Invalid debug tag persisted to disk", e);
+ continue;
+ }
+ debugTags.add(debugTag);
+ }
+ }
+
+ return debugTags;
+ }
}
/** Set of all tracked jobs. */
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 ae4e99cfeef3..e06006f25d3f 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
@@ -19,6 +19,9 @@ package com.android.server.job.controllers;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
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_WIFI;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -29,6 +32,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.job.JobInfo;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkPolicyListener;
@@ -40,6 +44,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.telephony.CellSignalStrength;
import android.telephony.SignalStrength;
import android.telephony.TelephonyCallback;
@@ -53,6 +58,7 @@ import android.util.Pools;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -117,6 +123,26 @@ public final class ConnectivityController extends RestrictingController implemen
| ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER
| ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED);
+ @VisibleForTesting
+ static final int TRANSPORT_AFFINITY_UNDEFINED = 0;
+ @VisibleForTesting
+ static final int TRANSPORT_AFFINITY_PREFER = 1;
+ @VisibleForTesting
+ static final int TRANSPORT_AFFINITY_AVOID = 2;
+ /**
+ * Set of affinities to different network transports. If a given network has multiple
+ * transports, the avoided ones take priority --- a network with an avoided transport
+ * should be avoided if possible, even if the network has preferred transports as well.
+ */
+ @VisibleForTesting
+ 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);
+ }
+
+ private final CcConfig mCcConfig;
private final ConnectivityManager mConnManager;
private final NetworkPolicyManager mNetPolicyManager;
private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
@@ -138,7 +164,7 @@ public final class ConnectivityController extends RestrictingController implemen
* latest capabilities to avoid unnecessary calls into ConnectivityManager.
*/
@GuardedBy("mLock")
- private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>();
+ private final ArrayMap<Network, CachedNetworkMetadata> mAvailableNetworks = new ArrayMap<>();
private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
new SparseArray<>();
@@ -267,6 +293,7 @@ public final class ConnectivityController extends RestrictingController implemen
@NonNull FlexibilityController flexibilityController) {
super(service);
mHandler = new CcHandler(AppSchedulingModuleThread.get().getLooper());
+ mCcConfig = new CcConfig();
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
@@ -279,6 +306,11 @@ public final class ConnectivityController extends RestrictingController implemen
mConnManager.registerNetworkCallback(request, mNetworkCallback);
mNetPolicyManager.registerListener(mNetPolicyListener);
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ // For now, we don't have network affinities on watches.
+ sNetworkTransportAffinities.clear();
+ }
}
@GuardedBy("mLock")
@@ -386,7 +418,9 @@ public final class ConnectivityController extends RestrictingController implemen
synchronized (mLock) {
for (int i = 0; i < mAvailableNetworks.size(); ++i) {
final Network network = mAvailableNetworks.keyAt(i);
- final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i);
+ final CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
+ final NetworkCapabilities capabilities =
+ metadata == null ? null : metadata.networkCapabilities;
final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
if (DEBUG) {
Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
@@ -589,6 +623,48 @@ public final class ConnectivityController extends RestrictingController implemen
mHandler.sendEmptyMessage(MSG_UPDATE_ALL_TRACKED_JOBS);
}
+ @Override
+ public void prepareForUpdatedConstantsLocked() {
+ mCcConfig.mShouldReprocessNetworkCapabilities = false;
+ mCcConfig.mFlexIsEnabled = mFlexibilityController.isEnabled();
+ }
+
+ @Override
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ mCcConfig.processConstantLocked(properties, key);
+ }
+
+ @Override
+ public void onConstantsUpdatedLocked() {
+ if (mCcConfig.mShouldReprocessNetworkCapabilities
+ || (mFlexibilityController.isEnabled() != mCcConfig.mFlexIsEnabled)) {
+ AppSchedulingModuleThread.getHandler().post(() -> {
+ boolean flexAffinitiesChanged = false;
+ boolean flexAffinitiesSatisfied = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mAvailableNetworks.size(); ++i) {
+ CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
+ if (metadata == null) {
+ continue;
+ }
+ if (updateTransportAffinitySatisfaction(metadata)) {
+ // Something changed. Update jobs.
+ flexAffinitiesChanged = true;
+ }
+ flexAffinitiesSatisfied |= metadata.satisfiesTransportAffinities;
+ }
+ if (flexAffinitiesChanged) {
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_CONNECTIVITY,
+ flexAffinitiesSatisfied, sElapsedRealtimeClock.millis());
+ updateAllTrackedJobsLocked(false);
+ }
+ }
+ });
+ }
+ }
+
private boolean isUsable(NetworkCapabilities capabilities) {
return capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
@@ -831,7 +907,7 @@ public final class ConnectivityController extends RestrictingController implemen
if (!constants.CONN_USE_CELL_SIGNAL_STRENGTH) {
return true;
}
- if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
return true;
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
@@ -988,6 +1064,68 @@ public final class ConnectivityController extends RestrictingController implemen
return false;
}
+ /**
+ * Updates {@link CachedNetworkMetadata#satisfiesTransportAffinities} in the given
+ * {@link CachedNetworkMetadata} object.
+ * @return true if the satisfaction changed
+ */
+ private boolean updateTransportAffinitySatisfaction(
+ @NonNull CachedNetworkMetadata cachedNetworkMetadata) {
+ final boolean satisfiesAffinities =
+ satisfiesTransportAffinities(cachedNetworkMetadata.networkCapabilities);
+ if (cachedNetworkMetadata.satisfiesTransportAffinities != satisfiesAffinities) {
+ cachedNetworkMetadata.satisfiesTransportAffinities = satisfiesAffinities;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean satisfiesTransportAffinities(@Nullable NetworkCapabilities capabilities) {
+ if (!mFlexibilityController.isEnabled()) {
+ return true;
+ }
+ if (capabilities == null) {
+ Slog.wtf(TAG, "Network constraint satisfied with null capabilities");
+ return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
+ }
+
+ if (sNetworkTransportAffinities.size() == 0) {
+ return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
+ }
+
+ final int[] transports = capabilities.getTransportTypes();
+ if (transports.length == 0) {
+ return !mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY;
+ }
+
+ for (int t : transports) {
+ int affinity = sNetworkTransportAffinities.get(t, TRANSPORT_AFFINITY_UNDEFINED);
+ if (DEBUG) {
+ Slog.d(TAG,
+ "satisfiesTransportAffinities transport=" + t + " aff=" + affinity);
+ }
+ switch (affinity) {
+ case TRANSPORT_AFFINITY_UNDEFINED:
+ if (mCcConfig.AVOID_UNDEFINED_TRANSPORT_AFFINITY) {
+ // Avoided transports take precedence.
+ // Return as soon as we encounter a transport to avoid.
+ return false;
+ }
+ break;
+ case TRANSPORT_AFFINITY_PREFER:
+ // Nothing to do here. We like this transport.
+ break;
+ case TRANSPORT_AFFINITY_AVOID:
+ // Avoided transports take precedence.
+ // Return as soon as we encounter a transport to avoid.
+ return false;
+ }
+ }
+
+ // Didn't see any transport to avoid.
+ return true;
+ }
+
@GuardedBy("mLock")
private void maybeRegisterDefaultNetworkCallbackLocked(JobStatus jobStatus) {
final int sourceUid = jobStatus.getSourceUid();
@@ -1172,6 +1310,12 @@ public final class ConnectivityController extends RestrictingController implemen
@Nullable
private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
+ final CachedNetworkMetadata metadata = getNetworkMetadata(network);
+ return metadata == null ? null : metadata.networkCapabilities;
+ }
+
+ @Nullable
+ private CachedNetworkMetadata getNetworkMetadata(@Nullable Network network) {
if (network == null) {
return null;
}
@@ -1234,14 +1378,16 @@ public final class ConnectivityController extends RestrictingController implemen
return updateConstraintsSatisfied(jobStatus, nowElapsed, null, null);
}
final Network network = getNetworkLocked(jobStatus);
- final NetworkCapabilities capabilities = getNetworkCapabilities(network);
- return updateConstraintsSatisfied(jobStatus, nowElapsed, network, capabilities);
+ final CachedNetworkMetadata networkMetadata = getNetworkMetadata(network);
+ return updateConstraintsSatisfied(jobStatus, nowElapsed, network, networkMetadata);
}
private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed,
- Network network, NetworkCapabilities capabilities) {
+ Network network, @Nullable CachedNetworkMetadata networkMetadata) {
// TODO: consider matching against non-default networks
+ final NetworkCapabilities capabilities =
+ networkMetadata == null ? null : networkMetadata.networkCapabilities;
final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants);
if (!satisfied && jobStatus.network != null
@@ -1263,10 +1409,10 @@ public final class ConnectivityController extends RestrictingController implemen
final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);
- if (jobStatus.getPreferUnmetered()) {
- jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
- && capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));
-
+ jobStatus.setTransportAffinitiesSatisfied(satisfied && networkMetadata != null
+ && networkMetadata.satisfiesTransportAffinities);
+ if (jobStatus.canApplyTransportAffinities()) {
+ // Only modify the flex constraint if the job actually needs it.
jobStatus.setFlexibilityConstraintSatisfied(nowElapsed,
mFlexibilityController.isFlexibilitySatisfiedLocked(jobStatus));
}
@@ -1367,7 +1513,6 @@ public final class ConnectivityController extends RestrictingController implemen
final JobStatus js = jobs.valueAt(i);
final Network net = getNetworkLocked(js);
- final NetworkCapabilities netCap = getNetworkCapabilities(net);
final boolean match = (filterNetwork == null
|| Objects.equals(filterNetwork, net));
@@ -1375,7 +1520,7 @@ public final class ConnectivityController extends RestrictingController implemen
// job hasn't yet been evaluated against the currently
// active network; typically when we just lost a network.
if (match || !Objects.equals(js.network, net)) {
- changed |= updateConstraintsSatisfied(js, nowElapsed, net, netCap);
+ changed |= updateConstraintsSatisfied(js, nowElapsed, net, getNetworkMetadata(net));
}
}
return changed;
@@ -1417,9 +1562,19 @@ public final class ConnectivityController extends RestrictingController implemen
Slog.v(TAG, "onCapabilitiesChanged: " + network);
}
synchronized (mLock) {
- final NetworkCapabilities oldCaps = mAvailableNetworks.put(network, capabilities);
- if (oldCaps != null) {
- maybeUnregisterSignalStrengthCallbackLocked(oldCaps);
+ CachedNetworkMetadata cnm = mAvailableNetworks.get(network);
+ if (cnm == null) {
+ cnm = new CachedNetworkMetadata();
+ mAvailableNetworks.put(network, cnm);
+ } else {
+ final NetworkCapabilities oldCaps = cnm.networkCapabilities;
+ if (oldCaps != null) {
+ maybeUnregisterSignalStrengthCallbackLocked(oldCaps);
+ }
+ }
+ cnm.networkCapabilities = capabilities;
+ if (updateTransportAffinitySatisfaction(cnm)) {
+ maybeUpdateFlexConstraintLocked(cnm);
}
maybeRegisterSignalStrengthCallbackLocked(capabilities);
updateTrackedJobsLocked(-1, network);
@@ -1433,9 +1588,14 @@ public final class ConnectivityController extends RestrictingController implemen
Slog.v(TAG, "onLost: " + network);
}
synchronized (mLock) {
- final NetworkCapabilities capabilities = mAvailableNetworks.remove(network);
- if (capabilities != null) {
- maybeUnregisterSignalStrengthCallbackLocked(capabilities);
+ final CachedNetworkMetadata cnm = mAvailableNetworks.remove(network);
+ if (cnm != null) {
+ if (cnm.networkCapabilities != null) {
+ maybeUnregisterSignalStrengthCallbackLocked(cnm.networkCapabilities);
+ }
+ if (cnm.satisfiesTransportAffinities) {
+ maybeUpdateFlexConstraintLocked(null);
+ }
}
for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) {
UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u);
@@ -1451,7 +1611,7 @@ public final class ConnectivityController extends RestrictingController implemen
@GuardedBy("mLock")
private void maybeRegisterSignalStrengthCallbackLocked(
@NonNull NetworkCapabilities capabilities) {
- if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
return;
}
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
@@ -1476,14 +1636,17 @@ public final class ConnectivityController extends RestrictingController implemen
@GuardedBy("mLock")
private void maybeUnregisterSignalStrengthCallbackLocked(
@NonNull NetworkCapabilities capabilities) {
- if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ if (!capabilities.hasTransport(TRANSPORT_CELLULAR)) {
return;
}
ArraySet<Integer> activeIds = new ArraySet<>();
for (int i = 0, size = mAvailableNetworks.size(); i < size; ++i) {
- NetworkCapabilities nc = mAvailableNetworks.valueAt(i);
- if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- activeIds.addAll(nc.getSubscriptionIds());
+ final CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
+ if (metadata == null || metadata.networkCapabilities == null) {
+ continue;
+ }
+ if (metadata.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ activeIds.addAll(metadata.networkCapabilities.getSubscriptionIds());
}
}
if (DEBUG) {
@@ -1504,6 +1667,37 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
}
+
+ /**
+ * Maybe call {@link FlexibilityController#setConstraintSatisfied(int, boolean, long)}
+ * if the network affinity state has changed.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateFlexConstraintLocked(
+ @Nullable CachedNetworkMetadata cachedNetworkMetadata) {
+ if (cachedNetworkMetadata != null
+ && cachedNetworkMetadata.satisfiesTransportAffinities) {
+ mFlexibilityController.setConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY,
+ true, sElapsedRealtimeClock.millis());
+ } else {
+ // This network doesn't satisfy transport affinities. Check if any other
+ // available networks do satisfy the affinities before saying that the
+ // transport affinity is no longer satisfied for flex.
+ boolean isTransportAffinitySatisfied = false;
+ for (int i = mAvailableNetworks.size() - 1; i >= 0; --i) {
+ final CachedNetworkMetadata cnm = mAvailableNetworks.valueAt(i);
+ if (cnm != null && cnm.satisfiesTransportAffinities) {
+ isTransportAffinitySatisfied = true;
+ break;
+ }
+ }
+ if (!isTransportAffinitySatisfied) {
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_CONNECTIVITY, false,
+ sElapsedRealtimeClock.millis());
+ }
+ }
+ }
};
private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
@@ -1573,6 +1767,57 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
+ @VisibleForTesting
+ class CcConfig {
+ private boolean mFlexIsEnabled = FlexibilityController.FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+ private boolean mShouldReprocessNetworkCapabilities = false;
+
+ /**
+ * Prefix to use with all constant keys in order to "sub-namespace" the keys.
+ * "conn_" is used for legacy reasons.
+ */
+ private static final String CC_CONFIG_PREFIX = "conn_";
+
+ @VisibleForTesting
+ static final String KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY =
+ CC_CONFIG_PREFIX + "avoid_undefined_transport_affinity";
+
+ private static final boolean DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY = false;
+
+ /**
+ * If true, will avoid network transports that don't have an explicitly defined affinity.
+ */
+ public boolean AVOID_UNDEFINED_TRANSPORT_AFFINITY =
+ DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY;
+
+ @GuardedBy("mLock")
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ switch (key) {
+ case KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY:
+ final boolean avoid = properties.getBoolean(key,
+ DEFAULT_AVOID_UNDEFINED_TRANSPORT_AFFINITY);
+ if (AVOID_UNDEFINED_TRANSPORT_AFFINITY != avoid) {
+ AVOID_UNDEFINED_TRANSPORT_AFFINITY = avoid;
+ mShouldReprocessNetworkCapabilities = true;
+ }
+ break;
+ }
+ }
+
+ private void dump(IndentingPrintWriter pw) {
+ pw.println();
+ pw.print(ConnectivityController.class.getSimpleName());
+ pw.println(":");
+ pw.increaseIndent();
+
+ pw.print(KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY,
+ AVOID_UNDEFINED_TRANSPORT_AFFINITY).println();
+
+ pw.decreaseIndent();
+ }
+ }
+
private class UidDefaultNetworkCallback extends NetworkCallback {
private int mUid;
@Nullable
@@ -1676,6 +1921,18 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
+ private static class CachedNetworkMetadata {
+ public NetworkCapabilities networkCapabilities;
+ public boolean satisfiesTransportAffinities;
+
+ public String toString() {
+ return "CNM{"
+ + networkCapabilities.toString()
+ + ", satisfiesTransportAffinities=" + satisfiesTransportAffinities
+ + "}";
+ }
+ }
+
private static class UidStats {
public final int uid;
public int baseBias;
@@ -1739,6 +1996,17 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
+ @VisibleForTesting
+ @NonNull
+ CcConfig getCcConfig() {
+ return mCcConfig;
+ }
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ mCcConfig.dump(pw);
+ }
+
@GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
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 0e03ea1ebe7d..fed3c42ab87f 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
@@ -43,6 +43,8 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.SparseLongArray;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -68,11 +70,6 @@ public final class FlexibilityController extends StateController {
| CONSTRAINT_CHARGING
| CONSTRAINT_IDLE;
- /** List of flexible constraints a job can opt into. */
- static final int OPTIONAL_FLEXIBLE_CONSTRAINTS = CONSTRAINT_BATTERY_NOT_LOW
- | CONSTRAINT_CHARGING
- | CONSTRAINT_IDLE;
-
/** List of all job flexible constraints whose satisfaction is job specific. */
private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;
@@ -83,9 +80,6 @@ public final class FlexibilityController extends StateController {
private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
- static final int NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(OPTIONAL_FLEXIBLE_CONSTRAINTS);
-
static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
@@ -103,6 +97,9 @@ public final class FlexibilityController extends StateController {
private long mRescheduledJobDeadline = FcConfig.DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
private long mMaxRescheduledDeadline = FcConfig.DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
+ private long mUnseenConstraintGracePeriodMs =
+ FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+
@VisibleForTesting
@GuardedBy("mLock")
boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
@@ -132,6 +129,9 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
int mSatisfiedFlexibleConstraints;
+ @GuardedBy("mLock")
+ private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray();
+
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
@@ -247,29 +247,79 @@ public final class FlexibilityController extends StateController {
mPrefetchLifeCycleStart.delete(userId);
}
+ boolean isEnabled() {
+ synchronized (mLock) {
+ return mFlexibilityEnabled;
+ }
+ }
+
/** Checks if the flexibility constraint is actively satisfied for a given job. */
@GuardedBy("mLock")
boolean isFlexibilitySatisfiedLocked(JobStatus js) {
return !mFlexibilityEnabled
|| mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP
- || getNumSatisfiedRequiredConstraintsLocked(js)
- >= js.getNumRequiredFlexibleConstraints()
+ || hasEnoughSatisfiedConstraintsLocked(js)
|| mService.isCurrentlyRunningLocked(js);
}
+ /**
+ * Returns whether there are enough constraints satisfied to allow running the job from flex's
+ * perspective. This takes into account unseen constraint combinations and expectations around
+ * whether additional constraints can ever be satisfied.
+ */
@VisibleForTesting
@GuardedBy("mLock")
- int getNumSatisfiedRequiredConstraintsLocked(JobStatus js) {
- return Integer.bitCount(mSatisfiedFlexibleConstraints)
- // Connectivity is job-specific, so must be handled separately.
- + (js.getHasAccessToUnmetered() ? 1 : 0);
+ boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) {
+ final int satisfiedConstraints = mSatisfiedFlexibleConstraints
+ & (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0));
+ final int numSatisfied = Integer.bitCount(satisfiedConstraints);
+ if (numSatisfied >= js.getNumRequiredFlexibleConstraints()) {
+ return true;
+ }
+ // We don't yet have the full number of required flex constraints. See if we should expect
+ // to be able to reach it. If not, then there's no point waiting anymore.
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed < mUnseenConstraintGracePeriodMs) {
+ // Too soon after boot. Not enough time to start predicting. Wait longer.
+ return false;
+ }
+
+ // The intention is to not force jobs to wait for constraint combinations that have never
+ // been seen together in a while. The job may still be allowed to wait for other constraint
+ // combinations. Thus, the logic is:
+ // If all the constraint combinations that have a count higher than the current satisfied
+ // count have not been seen recently enough, then assume they won't be seen anytime soon,
+ // so don't force the job to wait longer. If any combinations with a higher count have been
+ // seen recently, then the job can potentially wait for those combinations.
+ final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0));
+ for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) {
+ final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
+ if ((constraints & irrelevantConstraints) != 0) {
+ // Ignore combinations that couldn't satisfy this job's needs.
+ continue;
+ }
+ final long lastSeenElapsed = mLastSeenConstraintTimesElapsed.valueAt(i);
+ final boolean seenRecently =
+ nowElapsed - lastSeenElapsed <= mUnseenConstraintGracePeriodMs;
+ if (Integer.bitCount(constraints) > numSatisfied && seenRecently) {
+ // We've seen a set of constraints with a higher count than what is currently
+ // satisfied recently enough, which means we can expect to see it again at some
+ // point. Keep waiting for now.
+ return false;
+ }
+ }
+
+ // We haven't seen any constraint set with more satisfied than the current satisfied count.
+ // There's no reason to expect additional constraints to be satisfied. Let the job run.
+ return true;
}
/**
* Sets the controller's constraint to a given state.
* Changes flexibility constraint satisfaction for affected jobs.
*/
- @VisibleForTesting
void setConstraintSatisfied(int constraint, boolean state, long nowElapsed) {
synchronized (mLock) {
final boolean old = (mSatisfiedFlexibleConstraints & constraint) != 0;
@@ -279,14 +329,34 @@ public final class FlexibilityController extends StateController {
if (DEBUG) {
Slog.d(TAG, "setConstraintSatisfied: "
- + " constraint: " + constraint + " state: " + state);
+ + " constraint: " + constraint + " state: " + state);
+ }
+
+ // Mark now as the last time we saw this set of constraints.
+ mLastSeenConstraintTimesElapsed.put(mSatisfiedFlexibleConstraints, nowElapsed);
+ if (!state) {
+ // Mark now as the last time we saw this particular constraint.
+ // (Good for logging/dump purposes).
+ mLastSeenConstraintTimesElapsed.put(constraint, nowElapsed);
}
mSatisfiedFlexibleConstraints =
(mSatisfiedFlexibleConstraints & ~constraint) | (state ? constraint : 0);
- // Push the job update to the handler to avoid blocking other controllers and
- // potentially batch back-to-back controller state updates together.
- mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget();
+
+ if ((JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS & constraint) != 0) {
+ // Job-specific constraint --> don't need to proceed with logic below that
+ // works with system-wide constraints.
+ return;
+ }
+
+ if (mFlexibilityEnabled) {
+ // Only attempt to update jobs if the flex logic is enabled. Otherwise, the status
+ // of the jobs won't change, so all the work will be a waste.
+
+ // Push the job update to the handler to avoid blocking other controllers and
+ // potentially batch back-to-back controller state updates together.
+ mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget();
+ }
}
}
@@ -495,7 +565,7 @@ public final class FlexibilityController extends StateController {
final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
int toDrop = 0;
final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
- + (js.getPreferUnmetered() ? 1 : 0);
+ + (js.canApplyTransportAffinities() ? 1 : 0);
for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
if (curPercent >= mPercentToDropConstraints[i]) {
toDrop++;
@@ -536,7 +606,6 @@ public final class FlexibilityController extends StateController {
if (!predicate.test(js)) {
continue;
}
- pw.print("#");
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
@@ -638,7 +707,7 @@ public final class FlexibilityController extends StateController {
final long nowElapsed = sElapsedRealtimeClock.millis();
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
- for (int o = 0; o <= NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS; ++o) {
+ for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) {
final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
.getJobsByNumRequiredConstraints(o);
@@ -661,7 +730,6 @@ public final class FlexibilityController extends StateController {
}
}
- @VisibleForTesting
class FcConfig {
private boolean mShouldReevaluateConstraints = false;
@@ -681,8 +749,10 @@ public final class FlexibilityController extends StateController {
FC_CONFIG_PREFIX + "max_rescheduled_deadline_ms";
static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
+ static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
+ FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms";
- private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
+ static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
@VisibleForTesting
static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
@VisibleForTesting
@@ -692,6 +762,8 @@ public final class FlexibilityController extends StateController {
final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS;
private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS;
+ @VisibleForTesting
+ static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS;
/**
* If false the controller will not track new jobs
@@ -711,6 +783,11 @@ public final class FlexibilityController extends StateController {
public long RESCHEDULED_JOB_DEADLINE_MS = DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
/** The max deadline for rescheduled jobs. */
public long MAX_RESCHEDULED_DEADLINE_MS = DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
+ /**
+ * How long to wait after last seeing a constraint combination before no longer waiting for
+ * it in order to run jobs.
+ */
+ public long UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@@ -774,6 +851,14 @@ public final class FlexibilityController extends StateController {
mShouldReevaluateConstraints = true;
}
break;
+ case KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS:
+ UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
+ properties.getLong(key, DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS);
+ if (mUnseenConstraintGracePeriodMs != UNSEEN_CONSTRAINT_GRACE_PERIOD_MS) {
+ mUnseenConstraintGracePeriodMs = UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS:
String dropPercentString = properties.getString(key, "");
PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
@@ -828,6 +913,8 @@ public final class FlexibilityController extends StateController {
PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS).println();
pw.print(KEY_RESCHEDULED_JOB_DEADLINE_MS, RESCHEDULED_JOB_DEADLINE_MS).println();
pw.print(KEY_MAX_RESCHEDULED_DEADLINE_MS, MAX_RESCHEDULED_DEADLINE_MS).println();
+ pw.print(KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS, UNSEEN_CONSTRAINT_GRACE_PERIOD_MS)
+ .println();
pw.decreaseIndent();
}
@@ -848,12 +935,34 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
- pw.println("# Constraints Satisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints));
- pw.print("Satisfied Flexible Constraints: ");
+ pw.print("Satisfied Flexible Constraints:");
JobStatus.dumpConstraints(pw, mSatisfiedFlexibleConstraints);
pw.println();
pw.println();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ pw.println("Time since constraint combos last seen:");
+ pw.increaseIndent();
+ for (int i = 0; i < mLastSeenConstraintTimesElapsed.size(); ++i) {
+ final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
+ if (constraints == mSatisfiedFlexibleConstraints) {
+ pw.print("0ms");
+ } else {
+ TimeUtils.formatDuration(
+ mLastSeenConstraintTimesElapsed.valueAt(i), nowElapsed, pw);
+ }
+ pw.print(":");
+ if (constraints != 0) {
+ // dumpConstraints prepends with a space, so no need to add a space after the :
+ JobStatus.dumpConstraints(pw, constraints);
+ } else {
+ pw.print(" none");
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+
+ pw.println();
mFlexibilityTracker.dump(pw, predicate);
pw.println();
mFlexibilityAlarmQueue.dump(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index cb6cc2bd58aa..13bea6bd1dd1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
@@ -163,9 +162,6 @@ public final class JobStatus {
*/
private int mNumDroppedFlexibleConstraints;
- /** If the job is going to be passed an unmetered network. */
- private boolean mHasAccessToUnmetered;
-
/** If the effective bucket has been downgraded once due to being buggy. */
private boolean mIsDowngradedDueToBuggyApp;
@@ -562,11 +558,10 @@ public final class JobStatus {
/** The job's dynamic requirements have been satisfied. */
private boolean mReadyDynamicSatisfied;
- /**
- * The job prefers an unmetered network if it has the connectivity constraint but is
- * okay with any meteredness.
- */
- private final boolean mPreferUnmetered;
+ /** Whether to apply the optimization transport preference logic to this job. */
+ private final boolean mCanApplyTransportAffinities;
+ /** True if the optimization transport preference is satisfied for this job. */
+ private boolean mTransportAffinitiesSatisfied;
/** The reason a job most recently went from ready to not ready. */
private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
@@ -603,7 +598,6 @@ public final class JobStatus {
long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs,
int internalFlags,
int dynamicConstraints) {
- this.job = job;
this.callingUid = callingUid;
this.standbyBucket = standbyBucket;
mNamespace = namespace;
@@ -631,6 +625,22 @@ public final class JobStatus {
this.sourceTag = tag;
}
+ // This needs to be done before setting the field variable.
+ if (job.getRequiredNetwork() != null) {
+ // Later, when we check if a given network satisfies the required
+ // network, we need to know the UID that is requesting it, so push
+ // the source UID into place.
+ final JobInfo.Builder builder = new JobInfo.Builder(job);
+ builder.setRequiredNetwork(new NetworkRequest.Builder(job.getRequiredNetwork())
+ .setUids(Collections.singleton(new Range<>(this.sourceUid, this.sourceUid)))
+ .build());
+ // Don't perform validation checks at this point since we've already passed the
+ // initial validation check.
+ job = builder.build(false, false);
+ }
+
+ this.job = job;
+
final String bnNamespace = namespace == null ? "" : "@" + namespace + "@";
this.batteryName = this.sourceTag != null
? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
@@ -671,12 +681,12 @@ public final class JobStatus {
}
mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
- mPreferUnmetered = job.getRequiredNetwork() != null
- && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);
+ mCanApplyTransportAffinities = job.getRequiredNetwork() != null
+ && job.getRequiredNetwork().getTransportTypes().length == 0;
final boolean lacksSomeFlexibleConstraints =
((~requiredConstraints) & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS) != 0
- || mPreferUnmetered;
+ || mCanApplyTransportAffinities;
final boolean satisfiesMinWindowException =
(latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
>= MIN_WINDOW_FOR_FLEXIBILITY_MS;
@@ -688,7 +698,7 @@ public final class JobStatus {
&& (numFailures + numSystemStops) != 1
&& lacksSomeFlexibleConstraints) {
mNumRequiredFlexibleConstraints =
- NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
+ NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mCanApplyTransportAffinities ? 1 : 0);
requiredConstraints |= CONSTRAINT_FLEXIBLE;
} else {
mNumRequiredFlexibleConstraints = 0;
@@ -713,21 +723,6 @@ public final class JobStatus {
updateNetworkBytesLocked();
- if (job.getRequiredNetwork() != null) {
- // Later, when we check if a given network satisfies the required
- // network, we need to know the UID that is requesting it, so push
- // our source UID into place.
- final JobInfo.Builder builder = new JobInfo.Builder(job);
- final NetworkRequest.Builder requestBuilder =
- new NetworkRequest.Builder(job.getRequiredNetwork());
- requestBuilder.setUids(
- Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
- builder.setRequiredNetwork(requestBuilder.build());
- // Don't perform validation checks at this point since we've already passed the
- // initial validation check.
- job = builder.build(false, false);
- }
-
updateMediaBackupExemptionStatus();
}
@@ -1059,6 +1054,12 @@ public final class JobStatus {
return mLoggingJobId;
}
+ /** Returns a trace tag using debug information provided by the app. */
+ @Nullable
+ public String getAppTraceTag() {
+ return job.getTraceTag();
+ }
+
/** Returns whether this job was scheduled by one app on behalf of another. */
public boolean isProxyJob() {
return mIsProxyJob;
@@ -1585,17 +1586,16 @@ public final class JobStatus {
mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
}
- /** Sets the jobs access to an unmetered network. */
- void setHasAccessToUnmetered(boolean access) {
- mHasAccessToUnmetered = access;
+ boolean areTransportAffinitiesSatisfied() {
+ return mTransportAffinitiesSatisfied;
}
- boolean getHasAccessToUnmetered() {
- return mHasAccessToUnmetered;
+ void setTransportAffinitiesSatisfied(boolean isSatisfied) {
+ mTransportAffinitiesSatisfied = isSatisfied;
}
- boolean getPreferUnmetered() {
- return mPreferUnmetered;
+ boolean canApplyTransportAffinities() {
+ return mCanApplyTransportAffinities;
}
@JobParameters.StopReason
@@ -2769,6 +2769,15 @@ public final class JobStatus {
pw.println("Has late constraint");
}
+ if (job.getTraceTag() != null) {
+ pw.print("Trace tag: ");
+ pw.println(job.getTraceTag());
+ }
+ if (job.getDebugTags().size() > 0) {
+ pw.print("Debug tags: ");
+ pw.println(job.getDebugTags());
+ }
+
pw.decreaseIndent();
}
diff --git a/api/Android.bp b/api/Android.bp
index 4d56b3748881..d6c14fbdfae3 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -80,7 +80,9 @@ combined_apis {
"framework-location",
"framework-media",
"framework-mediaprovider",
+ "framework-nfc",
"framework-ondevicepersonalization",
+ "framework-pdf",
"framework-permission",
"framework-permission-s",
"framework-scheduling",
@@ -383,7 +385,10 @@ java_defaults {
stub_only_libs: ["framework-protos"],
impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
impl_library_visibility: ["//frameworks/base"],
- defaults_visibility: ["//frameworks/base/location"],
+ defaults_visibility: [
+ "//frameworks/base/location",
+ "//frameworks/base/nfc",
+ ],
plugins: ["error_prone_android_framework"],
errorprone: {
javacflags: [
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index f6f69291ce0e..28b2d4b5e3ee 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -635,6 +635,7 @@ java_defaults {
api_contributions: [
"framework-virtualization.stubs.source.test.api.contribution",
"framework-location.stubs.source.test.api.contribution",
+ "framework-nfc.stubs.source.test.api.contribution",
],
}
diff --git a/api/api.go b/api/api.go
index 8df6dab715ef..71b1e10d2f47 100644
--- a/api/api.go
+++ b/api/api.go
@@ -32,6 +32,7 @@ const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
const location = "framework-location"
+const nfc = "framework-nfc"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -43,7 +44,7 @@ var core_libraries_modules = []string{art, conscrypt, i18n}
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization, location}
+var non_updatable_modules = []string{virtualization, location, nfc}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
diff --git a/api/coverage/tools/Android.bp b/api/coverage/tools/Android.bp
new file mode 100644
index 000000000000..3e169120dc48
--- /dev/null
+++ b/api/coverage/tools/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+java_binary_host {
+ name: "extract-flagged-apis",
+ srcs: ["ExtractFlaggedApis.kt"],
+ main_class: "android.platform.coverage.ExtractFlaggedApisKt",
+ static_libs: [
+ "metalava-signature-reader",
+ "extract_flagged_apis_proto",
+ ],
+}
+
+java_library_host {
+ name: "extract_flagged_apis_proto",
+ srcs: ["extract_flagged_apis.proto"],
+ static_libs: ["libprotobuf-java-full"],
+ proto: {
+ type: "full",
+ },
+}
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
new file mode 100644
index 000000000000..948e64f22f20
--- /dev/null
+++ b/api/coverage/tools/ExtractFlaggedApis.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 android.platform.coverage
+
+import com.android.tools.metalava.model.text.ApiFile
+import java.io.File
+import java.io.FileWriter
+
+/** Usage: extract-flagged-apis <api text file> <output .pb file> */
+fun main(args: Array<String>) {
+ var cb = ApiFile.parseApi(listOf(File(args[0])))
+ val flagToApi = mutableMapOf<String, MutableList<String>>()
+ cb.getPackages()
+ .allTopLevelClasses()
+ .filter { it.methods().size > 0 }
+ .forEach {
+ for (method in it.methods()) {
+ val flagValue =
+ method.modifiers
+ .findAnnotation("android.annotation.FlaggedApi")
+ ?.findAttribute("value")
+ ?.value
+ ?.value()
+ if (flagValue != null && flagValue is String) {
+ val methodQualifiedName = "${it.qualifiedName()}.${method.name()}"
+ if (flagToApi.containsKey(flagValue)) {
+ flagToApi.get(flagValue)?.add(methodQualifiedName)
+ } else {
+ flagToApi.put(flagValue, mutableListOf(methodQualifiedName))
+ }
+ }
+ }
+ }
+ var builder = FlagApiMap.newBuilder()
+ for (flag in flagToApi.keys) {
+ var flaggedApis = FlaggedApis.newBuilder()
+ for (method in flagToApi.get(flag).orEmpty()) {
+ flaggedApis.addFlaggedApi(FlaggedApi.newBuilder().setQualifiedName(method))
+ }
+ builder.putFlagToApi(flag, flaggedApis.build())
+ }
+ val flagApiMap = builder.build()
+ FileWriter(args[1]).use { it.write(flagApiMap.toString()) }
+}
diff --git a/api/coverage/tools/extract_flagged_apis.proto b/api/coverage/tools/extract_flagged_apis.proto
new file mode 100644
index 000000000000..a858108a27b2
--- /dev/null
+++ b/api/coverage/tools/extract_flagged_apis.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.platform.coverage;
+
+option java_multiple_files = true;
+
+message FlagApiMap {
+ map<string, FlaggedApis> flag_to_api = 1;
+}
+
+message FlaggedApis {
+ repeated FlaggedApi flagged_api = 1;
+}
+
+message FlaggedApi {
+ string qualified_name = 1;
+}
+
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index 29a8dfa96a57..a4174ee6ae17 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,13 +1,3 @@
-// b/305195721
-android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7428: lint: Unresolved link/see tag "android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101]
-
// b/303477132
android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 2d235331a672..917529ec1dcf 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
@@ -32,6 +33,7 @@
#include <ftl/concat.h>
#include <ftl/optional.h>
+#include <gui/DisplayCaptureArgs.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
@@ -48,14 +50,17 @@ using namespace android;
#define COLORSPACE_DISPLAY_P3 2
void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
- fprintf(stderr,
- "usage: %s [-hp] [-d display-id] [FILENAME]\n"
- " -h: this message\n"
- " -p: save the file as a png.\n"
- " -d: specify the display ID to capture%s\n"
- " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
- "If FILENAME ends with .png it will be saved as a png.\n"
- "If FILENAME is not given, the results will be printed to stdout.\n",
+ fprintf(stderr, R"(
+usage: %s [-hp] [-d display-id] [FILENAME]
+ -h: this message
+ -p: save the file as a png.
+ -d: specify the display ID to capture%s
+ see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
+ --hint-for-seamless If set will use the hintForSeamless path in SF
+
+If FILENAME ends with .png it will be saved as a png.
+If FILENAME is not given, the results will be printed to stdout.
+)",
pname,
displayIdOpt
.transform([](DisplayId id) {
@@ -65,6 +70,21 @@ void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
.c_str());
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ HintForSeamless,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"png", no_argument, nullptr, 'p'},
+ {"help", no_argument, nullptr, 'h'},
+ {"hint-for-seamless", no_argument, nullptr, LongOpts::HintForSeamless},
+ {0, 0, 0, 0}};
+
static int32_t flinger2bitmapFormat(PixelFormat f)
{
switch (f) {
@@ -134,10 +154,11 @@ int main(int argc, char** argv)
return 1;
}
std::optional<DisplayId> displayIdOpt;
+ gui::CaptureArgs captureArgs;
const char* pname = argv[0];
bool png = false;
int c;
- while ((c = getopt(argc, argv, "phd:")) != -1) {
+ while ((c = getopt_long(argc, argv, "phd:", LONG_OPTIONS, nullptr)) != -1) {
switch (c) {
case 'p':
png = true;
@@ -165,6 +186,9 @@ int main(int argc, char** argv)
}
usage(pname, displayIdOpt);
return 1;
+ case LongOpts::HintForSeamless:
+ captureArgs.hintForSeamlessTransition = true;
+ break;
}
}
@@ -215,7 +239,7 @@ int main(int argc, char** argv)
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
+ ScreenshotClient::captureDisplay(*displayIdOpt, captureArgs, captureListener);
ScreenCaptureResults captureResults = captureListener->waitForResults();
if (!captureResults.fenceResult.ok()) {
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index 7659054119c8..ec2b1f4db521 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -283,7 +283,10 @@ static void configure(JNIEnv* env, jclass /* clazz */, jint handle, jint code,
std::vector<int32_t> configs = toVector(env, rawConfigs);
// Configure uinput device, with user specified code and value.
for (auto& config : configs) {
- ::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config);
+ if (::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config) < 0) {
+ ALOGE("Error configuring device (ioctl %d, value 0x%x): %s", code, config,
+ strerror(errno));
+ }
}
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index ad5e70f4ff0c..b0fa34c68092 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -160,9 +160,16 @@ public class Device {
switch (msg.what) {
case MSG_OPEN_UINPUT_DEVICE:
SomeArgs args = (SomeArgs) msg.obj;
- mPtr = nativeOpenUinputDevice((String) args.arg1, args.argi1, args.argi2,
+ String name = (String) args.arg1;
+ mPtr = nativeOpenUinputDevice(name, args.argi1, args.argi2,
args.argi3, args.argi4, args.argi5, (String) args.arg2,
new DeviceCallback());
+ if (mPtr == 0) {
+ RuntimeException ex = new RuntimeException(
+ "Could not create uinput device \"" + name + "\"");
+ Log.e(TAG, "Couldn't create uinput device, exiting.", ex);
+ throw ex;
+ }
break;
case MSG_INJECT_EVENT:
if (mPtr != 0) {
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9d8f1f400950..4498bc2a09d6 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -16,19 +16,10 @@
package com.android.commands.uinput;
-import android.util.JsonReader;
-import android.util.JsonToken;
-import android.util.Log;
import android.util.SparseArray;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -39,47 +30,45 @@ import src.com.android.commands.uinput.InputAbsInfo;
public class Event {
private static final String TAG = "UinputEvent";
- enum Command {
- REGISTER("register"),
- DELAY("delay"),
- INJECT("inject"),
- SYNC("sync");
+ public enum Command {
+ REGISTER,
+ DELAY,
+ INJECT,
+ SYNC,
+ }
- final String mCommandName;
+ // Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the
+ // kernel.
+ public static final int EV_KEY = 0x01;
+ public static final int EV_REL = 0x02;
+ public static final int EV_ABS = 0x03;
+ public static final int EV_MSC = 0x04;
+ public static final int EV_SW = 0x05;
+ public static final int EV_LED = 0x11;
+ public static final int EV_SND = 0x12;
+ public static final int EV_FF = 0x15;
+
+ public enum UinputControlCode {
+ UI_SET_EVBIT(100),
+ UI_SET_KEYBIT(101),
+ UI_SET_RELBIT(102),
+ UI_SET_ABSBIT(103),
+ UI_SET_MSCBIT(104),
+ UI_SET_LEDBIT(105),
+ UI_SET_SNDBIT(106),
+ UI_SET_FFBIT(107),
+ UI_SET_SWBIT(109),
+ UI_SET_PROPBIT(110);
- Command(String command) {
- mCommandName = command;
- }
- }
+ private final int mValue;
- private static final int EV_KEY = 0x01;
- private static final int EV_REL = 0x02;
- private static final int EV_ABS = 0x03;
- private static final int EV_MSC = 0x04;
- private static final int EV_SW = 0x05;
- private static final int EV_LED = 0x11;
- private static final int EV_SND = 0x12;
- private static final int EV_FF = 0x15;
-
- private enum UinputControlCode {
- UI_SET_EVBIT("UI_SET_EVBIT", 100),
- UI_SET_KEYBIT("UI_SET_KEYBIT", 101),
- UI_SET_RELBIT("UI_SET_RELBIT", 102),
- UI_SET_ABSBIT("UI_SET_ABSBIT", 103),
- UI_SET_MSCBIT("UI_SET_MSCBIT", 104),
- UI_SET_LEDBIT("UI_SET_LEDBIT", 105),
- UI_SET_SNDBIT("UI_SET_SNDBIT", 106),
- UI_SET_FFBIT("UI_SET_FFBIT", 107),
- UI_SET_SWBIT("UI_SET_SWBIT", 109),
- UI_SET_PROPBIT("UI_SET_PROPBIT", 110);
-
- final String mName;
- final int mValue;
-
- UinputControlCode(String name, int value) {
- this.mName = name;
+ UinputControlCode(int value) {
this.mValue = value;
}
+
+ public int getValue() {
+ return mValue;
+ }
}
// These constants come from "include/uapi/linux/input.h" in the kernel
@@ -104,9 +93,9 @@ public class Event {
private Bus mBus;
private int[] mInjections;
private SparseArray<int[]> mConfiguration;
- private int mDuration;
+ private int mDurationMillis;
private int mFfEffectsMax = 0;
- private String mInputport;
+ private String mInputPort;
private SparseArray<InputAbsInfo> mAbsInfo;
private String mSyncToken;
@@ -138,12 +127,20 @@ public class Event {
return mInjections;
}
+ /**
+ * Returns a {@link SparseArray} describing the event codes that should be registered for the
+ * device. The keys are uinput ioctl codes (such as those returned from {@link
+ * UinputControlCode#getValue()}, while the values are arrays of event codes to be enabled with
+ * those ioctls. For example, key 101 (corresponding to {@link UinputControlCode#UI_SET_KEYBIT})
+ * could have values 0x110 ({@code BTN_LEFT}, 0x111 ({@code BTN_RIGHT}), and 0x112
+ * ({@code BTN_MIDDLE}).
+ */
public SparseArray<int[]> getConfiguration() {
return mConfiguration;
}
- public int getDuration() {
- return mDuration;
+ public int getDurationMillis() {
+ return mDurationMillis;
}
public int getFfEffectsMax() {
@@ -155,7 +152,7 @@ public class Event {
}
public String getPort() {
- return mInputport;
+ return mInputPort;
}
public String getSyncToken() {
@@ -174,13 +171,13 @@ public class Event {
+ ", bus=" + mBus
+ ", events=" + Arrays.toString(mInjections)
+ ", configuration=" + mConfiguration
- + ", duration=" + mDuration
+ + ", duration=" + mDurationMillis + "ms"
+ ", ff_effects_max=" + mFfEffectsMax
- + ", port=" + mInputport
+ + ", port=" + mInputPort
+ "}";
}
- private static class Builder {
+ public static class Builder {
private Event mEvent;
Builder() {
@@ -191,15 +188,8 @@ public class Event {
mEvent.mId = id;
}
- private void setCommand(String command) {
- Objects.requireNonNull(command, "Command must not be null");
- for (Command cmd : Command.values()) {
- if (cmd.mCommandName.equals(command)) {
- mEvent.mCommand = cmd;
- return;
- }
- }
- throw new IllegalStateException("Unrecognized command: " + command);
+ public void setCommand(Command command) {
+ mEvent.mCommand = command;
}
public void setName(String name) {
@@ -210,6 +200,12 @@ public class Event {
mEvent.mInjections = events;
}
+ /**
+ * Sets the event codes that should be registered with a {@code register} command.
+ *
+ * @param configuration An array of ioctls and event codes, as described at
+ * {@link Event#getConfiguration()}.
+ */
public void setConfiguration(SparseArray<int[]> configuration) {
mEvent.mConfiguration = configuration;
}
@@ -226,8 +222,8 @@ public class Event {
mEvent.mBus = bus;
}
- public void setDuration(int duration) {
- mEvent.mDuration = duration;
+ public void setDurationMillis(int durationMillis) {
+ mEvent.mDurationMillis = durationMillis;
}
public void setFfEffectsMax(int ffEffectsMax) {
@@ -238,8 +234,8 @@ public class Event {
mEvent.mAbsInfo = absInfo;
}
- public void setInputport(String port) {
- mEvent.mInputport = port;
+ public void setInputPort(String port) {
+ mEvent.mInputPort = port;
}
public void setSyncToken(String syncToken) {
@@ -260,7 +256,7 @@ public class Event {
}
}
case DELAY -> {
- if (mEvent.mDuration <= 0) {
+ if (mEvent.mDurationMillis <= 0) {
throw new IllegalStateException("Delay has missing or invalid duration");
}
}
@@ -278,343 +274,4 @@ public class Event {
return mEvent;
}
}
-
- /**
- * A class that parses the JSON event format from an input stream to build device events.
- */
- public static class Reader {
- private JsonReader mReader;
-
- public Reader(InputStreamReader in) {
- mReader = new JsonReader(in);
- mReader.setLenient(true);
- }
-
- /**
- * Get next event entry from JSON file reader.
- */
- public Event getNextEvent() throws IOException {
- Event e = null;
- while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
- Event.Builder eb = new Event.Builder();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "id":
- eb.setId(readInt());
- break;
- case "command":
- eb.setCommand(mReader.nextString());
- break;
- case "name":
- eb.setName(mReader.nextString());
- break;
- case "vid":
- eb.setVid(readInt());
- break;
- case "pid":
- eb.setPid(readInt());
- break;
- case "bus":
- eb.setBus(readBus());
- break;
- case "events":
- int[] injections = readInjectedEvents().stream()
- .mapToInt(Integer::intValue).toArray();
- eb.setInjections(injections);
- break;
- case "configuration":
- eb.setConfiguration(readConfiguration());
- break;
- case "ff_effects_max":
- eb.setFfEffectsMax(readInt());
- break;
- case "abs_info":
- eb.setAbsInfo(readAbsInfoArray());
- break;
- case "duration":
- eb.setDuration(readInt());
- break;
- case "port":
- eb.setInputport(mReader.nextString());
- break;
- case "syncToken":
- eb.setSyncToken(mReader.nextString());
- break;
- default:
- mReader.skipValue();
- }
- }
- mReader.endObject();
- } catch (IllegalStateException ex) {
- error("Error reading in object, ignoring.", ex);
- consumeRemainingElements();
- mReader.endObject();
- continue;
- }
- e = eb.build();
- }
-
- return e;
- }
-
- private ArrayList<Integer> readInjectedEvents() throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- // Read events in groups of three, because we expect an event type, event code,
- // and event value.
- final int type = readEvdevEventType();
- data.add(type);
- data.add(readEvdevEventCode(type));
- data.add(readInt());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
- switch (mReader.peek()) {
- case NUMBER: {
- return mReader.nextInt();
- }
- case STRING: {
- final var str = mReader.nextString();
- try {
- // Attempt to first parse the value as an int.
- return Integer.decode(str);
- } catch (NumberFormatException e) {
- // Then fall back to the supplied function.
- return stringToInt.apply(str);
- }
- }
- default: {
- throw new IllegalStateException(
- "Encountered malformed data. Expected int or string.");
- }
- }
- }
-
- private int readInt() throws IOException {
- return readValueAsInt((str) -> {
- throw new IllegalStateException("Encountered malformed data. Expected int.");
- });
- }
-
- private Bus readBus() throws IOException {
- String val = mReader.nextString();
- return Bus.valueOf(val.toUpperCase());
- }
-
- private SparseArray<int[]> readConfiguration()
- throws IllegalStateException, IOException {
- SparseArray<int[]> configuration = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- UinputControlCode controlCode = null;
- IntStream data = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "type":
- controlCode = readUinputControlCode();
- break;
- case "data":
- Objects.requireNonNull(controlCode,
- "Configuration 'type' must be specified before 'data'.");
- data = readDataForControlCode(controlCode)
- .stream().mapToInt(Integer::intValue);
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException(
- "Invalid key in device configuration: " + name);
- }
- }
- mReader.endObject();
- if (controlCode != null && data != null) {
- final int[] existing = configuration.get(controlCode.mValue);
- configuration.put(controlCode.mValue, existing == null ? data.toArray()
- : IntStream.concat(IntStream.of(existing), data).toArray());
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return configuration;
- }
-
- private UinputControlCode readUinputControlCode() throws IOException {
- var code = readValueAsInt((controlTypeStr) -> {
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mName.equals(controlTypeStr)) {
- return controlCode.mValue;
- }
- }
- return -1;
- });
- for (UinputControlCode controlCode : UinputControlCode.values()) {
- if (controlCode.mValue == code) {
- return controlCode;
- }
- }
- return null;
- }
-
- private List<Integer> readDataForControlCode(
- UinputControlCode controlCode) throws IOException {
- return switch (controlCode) {
- case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
- case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_KEY));
- case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_REL));
- case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_ABS));
- case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_MSC));
- case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_LED));
- case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SND));
- case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_FF));
- case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SW));
- case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
- };
- }
-
- interface IntValueReader {
- int readNextValue() throws IOException;
- }
-
- private ArrayList<Integer> readArrayAsInts(
- IntValueReader nextValueReader) throws IOException {
- ArrayList<Integer> data = new ArrayList<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- data.add(nextValueReader.readNextValue());
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return data;
- }
-
- private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
- InputAbsInfo absInfo = new InputAbsInfo();
- try {
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "value":
- absInfo.value = readInt();
- break;
- case "minimum":
- absInfo.minimum = readInt();
- break;
- case "maximum":
- absInfo.maximum = readInt();
- break;
- case "fuzz":
- absInfo.fuzz = readInt();
- break;
- case "flat":
- absInfo.flat = readInt();
- break;
- case "resolution":
- absInfo.resolution = readInt();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info: " + name);
- }
- }
- mReader.endObject();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return absInfo;
- }
-
- private SparseArray<InputAbsInfo> readAbsInfoArray()
- throws IllegalStateException, IOException {
- SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
- try {
- mReader.beginArray();
- while (mReader.hasNext()) {
- int type = 0;
- InputAbsInfo absInfo = null;
- mReader.beginObject();
- while (mReader.hasNext()) {
- String name = mReader.nextName();
- switch (name) {
- case "code":
- type = readEvdevEventCode(EV_ABS);
- break;
- case "info":
- absInfo = readAbsInfo();
- break;
- default:
- consumeRemainingElements();
- mReader.endObject();
- throw new IllegalStateException("Invalid key in abs info array: "
- + name);
- }
- }
- mReader.endObject();
- if (absInfo != null) {
- infoArray.put(type, absInfo);
- }
- }
- mReader.endArray();
- } catch (IllegalStateException | NumberFormatException e) {
- consumeRemainingElements();
- mReader.endArray();
- throw new IllegalStateException("Encountered malformed data.", e);
- }
- return infoArray;
- }
-
- private int readEvdevEventType() throws IOException {
- return readValueAsInt(Device::getEvdevEventTypeByLabel);
- }
-
- private int readEvdevEventCode(int type) throws IOException {
- return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
- }
-
- private int readEvdevInputProp() throws IOException {
- return readValueAsInt(Device::getEvdevInputPropByLabel);
- }
-
- private void consumeRemainingElements() throws IOException {
- while (mReader.hasNext()) {
- mReader.skipValue();
- }
- }
- }
-
- private static void error(String msg, Exception e) {
- System.out.println(msg);
- Log.e(TAG, msg);
- if (e != null) {
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
new file mode 100644
index 000000000000..a2195c7809be
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -0,0 +1,334 @@
+/*
+ * 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 com.android.commands.uinput;
+
+import android.util.JsonReader;
+import android.util.JsonToken;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * A class that parses the JSON-like event format described in the README to build {@link Event}s.
+ */
+public class JsonStyleParser {
+ private static final String TAG = "UinputJsonStyleParser";
+
+ private JsonReader mReader;
+
+ public JsonStyleParser(InputStreamReader in) {
+ mReader = new JsonReader(in);
+ mReader.setLenient(true);
+ }
+
+ /**
+ * Gets the next event entry from the JSON file.
+ */
+ public Event getNextEvent() throws IOException {
+ Event e = null;
+ while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
+ Event.Builder eb = new Event.Builder();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id" -> eb.setId(readInt());
+ case "command" -> eb.setCommand(
+ Event.Command.valueOf(mReader.nextString().toUpperCase()));
+ case "name" -> eb.setName(mReader.nextString());
+ case "vid" -> eb.setVid(readInt());
+ case "pid" -> eb.setPid(readInt());
+ case "bus" -> eb.setBus(readBus());
+ case "events" -> {
+ int[] injections = readInjectedEvents().stream()
+ .mapToInt(Integer::intValue).toArray();
+ eb.setInjections(injections);
+ }
+ case "configuration" -> eb.setConfiguration(readConfiguration());
+ case "ff_effects_max" -> eb.setFfEffectsMax(readInt());
+ case "abs_info" -> eb.setAbsInfo(readAbsInfoArray());
+ case "duration" -> eb.setDurationMillis(readInt());
+ case "port" -> eb.setInputPort(mReader.nextString());
+ case "syncToken" -> eb.setSyncToken(mReader.nextString());
+ default -> mReader.skipValue();
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException ex) {
+ error("Error reading in object, ignoring.", ex);
+ consumeRemainingElements();
+ mReader.endObject();
+ continue;
+ }
+ e = eb.build();
+ }
+
+ return e;
+ }
+
+ private ArrayList<Integer> readInjectedEvents() throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ // Read events in groups of three, because we expect an event type, event code,
+ // and event value.
+ final int type = readEvdevEventType();
+ data.add(type);
+ data.add(readEvdevEventCode(type));
+ data.add(readInt());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
+ switch (mReader.peek()) {
+ case NUMBER: {
+ return mReader.nextInt();
+ }
+ case STRING: {
+ final var str = mReader.nextString();
+ try {
+ // Attempt to first parse the value as an int.
+ return Integer.decode(str);
+ } catch (NumberFormatException e) {
+ // Then fall back to the supplied function.
+ return stringToInt.apply(str);
+ }
+ }
+ default: {
+ throw new IllegalStateException(
+ "Encountered malformed data. Expected int or string.");
+ }
+ }
+ }
+
+ private int readInt() throws IOException {
+ return readValueAsInt((str) -> {
+ throw new IllegalStateException("Encountered malformed data. Expected int.");
+ });
+ }
+
+ private Event.Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Event.Bus.valueOf(val.toUpperCase());
+ }
+
+ private SparseArray<int[]> readConfiguration()
+ throws IllegalStateException, IOException {
+ SparseArray<int[]> configuration = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ Event.UinputControlCode controlCode = null;
+ IntStream data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "type":
+ controlCode = readUinputControlCode();
+ break;
+ case "data":
+ Objects.requireNonNull(controlCode,
+ "Configuration 'type' must be specified before 'data'.");
+ data = readDataForControlCode(controlCode)
+ .stream().mapToInt(Integer::intValue);
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException(
+ "Invalid key in device configuration: " + name);
+ }
+ }
+ mReader.endObject();
+ if (controlCode != null && data != null) {
+ final int[] existing = configuration.get(controlCode.getValue());
+ configuration.put(controlCode.getValue(), existing == null ? data.toArray()
+ : IntStream.concat(IntStream.of(existing), data).toArray());
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return configuration;
+ }
+
+ private Event.UinputControlCode readUinputControlCode() throws IOException {
+ var code = readValueAsInt((controlTypeStr) -> {
+ try {
+ return Event.UinputControlCode.valueOf(controlTypeStr).getValue();
+ } catch (IllegalArgumentException ex) {
+ return -1;
+ }
+ });
+ for (Event.UinputControlCode controlCode : Event.UinputControlCode.values()) {
+ if (controlCode.getValue() == code) {
+ return controlCode;
+ }
+ }
+ return null;
+ }
+
+ private List<Integer> readDataForControlCode(
+ Event.UinputControlCode controlCode) throws IOException {
+ return switch (controlCode) {
+ case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
+ case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_KEY));
+ case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_REL));
+ case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_ABS));
+ case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_MSC));
+ case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_LED));
+ case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SND));
+ case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_FF));
+ case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(Event.EV_SW));
+ case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
+ };
+ }
+
+ interface IntValueReader {
+ int readNextValue() throws IOException;
+ }
+
+ private ArrayList<Integer> readArrayAsInts(
+ IntValueReader nextValueReader) throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ data.add(nextValueReader.readNextValue());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
+ InputAbsInfo absInfo = new InputAbsInfo();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "value" -> absInfo.value = readInt();
+ case "minimum" -> absInfo.minimum = readInt();
+ case "maximum" -> absInfo.maximum = readInt();
+ case "fuzz" -> absInfo.fuzz = readInt();
+ case "flat" -> absInfo.flat = readInt();
+ case "resolution" -> absInfo.resolution = readInt();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info: " + name);
+ }
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return absInfo;
+ }
+
+ private SparseArray<InputAbsInfo> readAbsInfoArray()
+ throws IllegalStateException, IOException {
+ SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ int type = 0;
+ InputAbsInfo absInfo = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "code" -> type = readEvdevEventCode(Event.EV_ABS);
+ case "info" -> absInfo = readAbsInfo();
+ default -> {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info array: "
+ + name);
+ }
+ }
+ }
+ mReader.endObject();
+ if (absInfo != null) {
+ infoArray.put(type, absInfo);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return infoArray;
+ }
+
+ private int readEvdevEventType() throws IOException {
+ return readValueAsInt(Device::getEvdevEventTypeByLabel);
+ }
+
+ private int readEvdevEventCode(int type) throws IOException {
+ return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
+ }
+
+ private int readEvdevInputProp() throws IOException {
+ return readValueAsInt(Device::getEvdevInputPropByLabel);
+ }
+
+ private void consumeRemainingElements() throws IOException {
+ while (mReader.hasNext()) {
+ mReader.skipValue();
+ }
+ }
+
+ private static void error(String msg, Exception e) {
+ System.out.println(msg);
+ Log.e(TAG, msg);
+ if (e != null) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 47b7a354f330..fe76abb9861d 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -35,7 +35,7 @@ import java.util.Objects;
public class Uinput {
private static final String TAG = "UINPUT";
- private final Event.Reader mReader;
+ private final JsonStyleParser mParser;
private final SparseArray<Device> mDevices;
private static void usage() {
@@ -74,7 +74,7 @@ public class Uinput {
private Uinput(InputStream in) {
mDevices = new SparseArray<Device>();
try {
- mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
+ mParser = new JsonStyleParser(new InputStreamReader(in, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
@@ -83,7 +83,7 @@ public class Uinput {
private void run() {
try {
Event e = null;
- while ((e = mReader.getNextEvent()) != null) {
+ while ((e = mParser.getNextEvent()) != null) {
process(e);
}
} catch (IOException ex) {
@@ -111,7 +111,7 @@ public class Uinput {
case REGISTER ->
error("Device id=" + e.getId() + " is already registered. Ignoring event.");
case INJECT -> d.injectEvent(e.getInjections());
- case DELAY -> d.addDelay(e.getDuration());
+ case DELAY -> d.addDelay(e.getDurationMillis());
case SYNC -> d.syncEvent(e.getSyncToken());
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index d75aafb3fef1..207abb216c37 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5316,21 +5316,23 @@ package android.app {
method public android.net.Uri getConditionId();
method @Nullable public android.content.ComponentName getConfigurationActivity();
method public long getCreationTime();
+ method @FlaggedApi("android.app.modes_api") @Nullable public android.service.notification.ZenDeviceEffects getDeviceEffects();
method @FlaggedApi("android.app.modes_api") @DrawableRes public int getIconResId();
method public int getInterruptionFilter();
method public String getName();
method public android.content.ComponentName getOwner();
method @FlaggedApi("android.app.modes_api") @Nullable public String getTriggerDescription();
method @FlaggedApi("android.app.modes_api") public int getType();
- method public android.service.notification.ZenPolicy getZenPolicy();
+ method @Nullable public android.service.notification.ZenPolicy getZenPolicy();
method public boolean isEnabled();
method @FlaggedApi("android.app.modes_api") public boolean isManualInvocationAllowed();
method public void setConditionId(android.net.Uri);
method public void setConfigurationActivity(@Nullable android.content.ComponentName);
+ method @FlaggedApi("android.app.modes_api") public void setDeviceEffects(@Nullable android.service.notification.ZenDeviceEffects);
method public void setEnabled(boolean);
method public void setInterruptionFilter(int);
method public void setName(String);
- method public void setZenPolicy(android.service.notification.ZenPolicy);
+ method public void setZenPolicy(@Nullable android.service.notification.ZenPolicy);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
field @FlaggedApi("android.app.modes_api") public static final int TYPE_BEDTIME = 3; // 0x3
@@ -5350,6 +5352,7 @@ package android.app {
method @NonNull public android.app.AutomaticZenRule build();
method @NonNull public android.app.AutomaticZenRule.Builder setConditionId(@NonNull android.net.Uri);
method @NonNull public android.app.AutomaticZenRule.Builder setConfigurationActivity(@Nullable android.content.ComponentName);
+ method @NonNull public android.app.AutomaticZenRule.Builder setDeviceEffects(@Nullable android.service.notification.ZenDeviceEffects);
method @NonNull public android.app.AutomaticZenRule.Builder setEnabled(boolean);
method @NonNull public android.app.AutomaticZenRule.Builder setIconResId(@DrawableRes int);
method @NonNull public android.app.AutomaticZenRule.Builder setInterruptionFilter(int);
@@ -6901,6 +6904,7 @@ package android.app {
public class NotificationManager {
method public String addAutomaticZenRule(android.app.AutomaticZenRule);
+ method @FlaggedApi("android.app.modes_api") public boolean areAutomaticZenRulesUserManaged();
method @Deprecated public boolean areBubblesAllowed();
method public boolean areBubblesEnabled();
method public boolean areNotificationsEnabled();
@@ -6952,6 +6956,8 @@ package android.app {
field public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED";
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+ field @FlaggedApi("android.app.modes_api") public static final int AUTOMATIC_RULE_STATUS_ACTIVATED = 4; // 0x4
+ field @FlaggedApi("android.app.modes_api") public static final int AUTOMATIC_RULE_STATUS_DEACTIVATED = 5; // 0x5
field public static final int AUTOMATIC_RULE_STATUS_DISABLED = 2; // 0x2
field public static final int AUTOMATIC_RULE_STATUS_ENABLED = 1; // 0x1
field public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3; // 0x3
@@ -7859,6 +7865,7 @@ package android.app.admin {
field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
+ field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
}
@@ -8852,6 +8859,7 @@ package android.app.job {
method public int getBackoffPolicy();
method @Nullable public android.content.ClipData getClipData();
method public int getClipGrantFlags();
+ method @FlaggedApi("android.app.job.job_debug_info_apis") @NonNull public java.util.Set<java.lang.String> getDebugTags();
method public long getEstimatedNetworkDownloadBytes();
method public long getEstimatedNetworkUploadBytes();
method @NonNull public android.os.PersistableBundle getExtras();
@@ -8868,6 +8876,7 @@ package android.app.job {
method public int getPriority();
method @Nullable public android.net.NetworkRequest getRequiredNetwork();
method @NonNull public android.content.ComponentName getService();
+ method @FlaggedApi("android.app.job.job_debug_info_apis") @Nullable public String getTraceTag();
method @NonNull public android.os.Bundle getTransientExtras();
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
@@ -8904,8 +8913,10 @@ package android.app.job {
public static final class JobInfo.Builder {
ctor public JobInfo.Builder(int, @NonNull android.content.ComponentName);
+ method @FlaggedApi("android.app.job.job_debug_info_apis") @NonNull public android.app.job.JobInfo.Builder addDebugTag(@NonNull String);
method public android.app.job.JobInfo.Builder addTriggerContentUri(@NonNull android.app.job.JobInfo.TriggerContentUri);
method public android.app.job.JobInfo build();
+ method @FlaggedApi("android.app.job.job_debug_info_apis") @NonNull public android.app.job.JobInfo.Builder removeDebugTag(@NonNull String);
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int);
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
@@ -8926,6 +8937,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean);
method public android.app.job.JobInfo.Builder setRequiresStorageNotLow(boolean);
+ method @FlaggedApi("android.app.job.job_debug_info_apis") @NonNull public android.app.job.JobInfo.Builder setTraceTag(@Nullable String);
method public android.app.job.JobInfo.Builder setTransientExtras(@NonNull android.os.Bundle);
method public android.app.job.JobInfo.Builder setTriggerContentMaxDelay(long);
method public android.app.job.JobInfo.Builder setTriggerContentUpdateDelay(long);
@@ -10399,7 +10411,7 @@ package android.content {
method public abstract java.io.File getDatabasePath(String);
method public int getDeviceId();
method public abstract java.io.File getDir(String, int);
- method @Nullable public android.view.Display getDisplay();
+ method @NonNull public android.view.Display getDisplay();
method @Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int);
method @Nullable public abstract java.io.File getExternalCacheDir();
method public abstract java.io.File[] getExternalCacheDirs();
@@ -12842,6 +12854,7 @@ package android.content.pm {
field public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
field public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
+ field @FlaggedApi("android.view.inputmethod.concurrent_input_methods") public static final String FEATURE_CONCURRENT_INPUT_METHODS = "android.software.concurrent_input_methods";
field @Deprecated public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final String FEATURE_CONTROLS = "android.software.controls";
@@ -14697,31 +14710,31 @@ package android.database.sqlite {
}
@FlaggedApi("android.database.sqlite.sqlite_apis_35") public final class SQLiteRawStatement implements java.io.Closeable {
- method public void bindBlob(int, @NonNull byte[]) throws android.database.sqlite.SQLiteException;
- method public void bindBlob(int, @NonNull byte[], int, int) throws android.database.sqlite.SQLiteException;
- method public void bindDouble(int, double) throws android.database.sqlite.SQLiteException;
- method public void bindInt(int, int) throws android.database.sqlite.SQLiteException;
- method public void bindLong(int, long) throws android.database.sqlite.SQLiteException;
- method public void bindNull(int) throws android.database.sqlite.SQLiteException;
- method public void bindText(int, @NonNull String) throws android.database.sqlite.SQLiteException;
+ method public void bindBlob(int, @NonNull byte[]);
+ method public void bindBlob(int, @NonNull byte[], int, int);
+ method public void bindDouble(int, double);
+ method public void bindInt(int, int);
+ method public void bindLong(int, long);
+ method public void bindNull(int);
+ method public void bindText(int, @NonNull String);
method public void clearBindings();
method public void close();
- method @Nullable public byte[] getColumnBlob(int) throws android.database.sqlite.SQLiteException;
- method public double getColumnDouble(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnInt(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnLength(int) throws android.database.sqlite.SQLiteException;
- method public long getColumnLong(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnName(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnText(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnType(int) throws android.database.sqlite.SQLiteException;
+ method @Nullable public byte[] getColumnBlob(int);
+ method public double getColumnDouble(int);
+ method public int getColumnInt(int);
+ method public int getColumnLength(int);
+ method public long getColumnLong(int);
+ method @NonNull public String getColumnName(int);
+ method @NonNull public String getColumnText(int);
+ method public int getColumnType(int);
method public int getParameterCount();
method public int getParameterIndex(@NonNull String);
method @Nullable public String getParameterName(int);
method public int getResultColumnCount();
method public boolean isOpen();
- method public int readColumnBlob(int, @NonNull byte[], int, int, int) throws android.database.sqlite.SQLiteException;
+ method public int readColumnBlob(int, @NonNull byte[], int, int, int);
method public void reset();
- method public boolean step() throws android.database.sqlite.SQLiteException;
+ method public boolean step();
field public static final int SQLITE_DATA_TYPE_BLOB = 4; // 0x4
field public static final int SQLITE_DATA_TYPE_FLOAT = 2; // 0x2
field public static final int SQLITE_DATA_TYPE_INTEGER = 1; // 0x1
@@ -22802,11 +22815,11 @@ package android.media {
method public void clearOnSessionLostStateListener();
method public void close();
method public void closeSession(@NonNull byte[]);
- method @android.media.MediaDrm.HdcpLevel public int getConnectedHdcpLevel();
+ method public int getConnectedHdcpLevel();
method public android.media.MediaDrm.CryptoSession getCryptoSession(@NonNull byte[], @NonNull String, @NonNull String);
method @NonNull public android.media.MediaDrm.KeyRequest getKeyRequest(@NonNull byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.HashMap<java.lang.String,java.lang.String>) throws android.media.NotProvisionedException;
method @NonNull public java.util.List<android.media.MediaDrm.LogMessage> getLogMessages();
- method @android.media.MediaDrm.HdcpLevel public int getMaxHdcpLevel();
+ method public int getMaxHdcpLevel();
method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
method public android.os.PersistableBundle getMetrics();
@@ -22820,13 +22833,13 @@ package android.media {
method @Deprecated @NonNull public byte[] getSecureStop(@NonNull byte[]);
method @Deprecated @NonNull public java.util.List<byte[]> getSecureStopIds();
method @Deprecated @NonNull public java.util.List<byte[]> getSecureStops();
- method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
+ method public int getSecurityLevel(@NonNull byte[]);
method @NonNull public static java.util.List<java.util.UUID> getSupportedCryptoSchemes();
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
- method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
+ method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, int);
method @NonNull public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
- method @NonNull public byte[] openSession(@android.media.MediaDrm.SecurityLevel int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
+ method @NonNull public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @Nullable public byte[] provideKeyResponse(@NonNull byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
method public void provideProvisionResponse(@NonNull byte[]) throws android.media.DeniedByServerException;
method @NonNull public java.util.HashMap<java.lang.String,java.lang.String> queryKeyStatus(@NonNull byte[]);
@@ -22838,7 +22851,7 @@ package android.media {
method public void removeOfflineLicense(@NonNull byte[]);
method @Deprecated public void removeSecureStop(@NonNull byte[]);
method public boolean requiresSecureDecoder(@NonNull String);
- method public boolean requiresSecureDecoder(@NonNull String, @android.media.MediaDrm.SecurityLevel int);
+ method public boolean requiresSecureDecoder(@NonNull String, int);
method public void restoreKeys(@NonNull byte[], @NonNull byte[]);
method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener);
method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener, @Nullable android.os.Handler);
@@ -22927,9 +22940,6 @@ package android.media {
field public static final int ERROR_ZERO_SUBSAMPLES = 33; // 0x21
}
- @Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel {
- }
-
public static final class MediaDrm.KeyRequest {
method @NonNull public byte[] getData();
method @NonNull public String getDefaultUrl();
@@ -23031,9 +23041,6 @@ package android.media {
method @NonNull public String getDefaultUrl();
}
- @Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
- }
-
public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
@@ -28696,14 +28703,17 @@ package android.nfc {
}
public final class NfcAdapter {
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction();
method public void disableForegroundDispatch(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction();
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
@@ -28811,6 +28821,7 @@ package android.nfc.cardemulation {
method public boolean removeAidsForService(android.content.ComponentName, String);
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);
@@ -28830,9 +28841,20 @@ 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 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 POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+ 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 String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+ 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";
}
@@ -33163,6 +33185,7 @@ package android.os {
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+ method @FlaggedApi("android.os.allow_thermal_headroom_thresholds") @NonNull public java.util.Map<java.lang.Integer,java.lang.Float> getThermalHeadroomThresholds();
method public boolean isAllowedInLowPowerStandby(int);
method public boolean isAllowedInLowPowerStandby(@NonNull String);
method public boolean isBatteryDischargePredictionPersonalized();
@@ -37271,6 +37294,7 @@ package android.provider {
}
public static final class Telephony.Carriers implements android.provider.BaseColumns {
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String ALWAYS_ON = "always_on";
field public static final String APN = "apn";
field public static final String AUTH_TYPE = "authtype";
field @Deprecated public static final String BEARER = "bearer";
@@ -37284,6 +37308,8 @@ package android.provider {
field public static final String MMSPORT = "mmsport";
field public static final String MMSPROXY = "mmsproxy";
field @Deprecated public static final String MNC = "mnc";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String MTU_V4 = "mtu_v4";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String MTU_V6 = "mtu_v6";
field @Deprecated public static final String MVNO_MATCH_DATA = "mvno_match_data";
field @Deprecated public static final String MVNO_TYPE = "mvno_type";
field public static final String NAME = "name";
@@ -37299,6 +37325,8 @@ package android.provider {
field public static final String SUBSCRIPTION_ID = "sub_id";
field public static final String TYPE = "type";
field public static final String USER = "user";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String USER_EDITABLE = "user_editable";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String USER_VISIBLE = "user_visible";
}
public static final class Telephony.Mms implements android.provider.Telephony.BaseMmsColumns {
@@ -39219,7 +39247,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("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...);
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @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);
@@ -40505,19 +40533,26 @@ package android.service.notification {
public final class Condition implements android.os.Parcelable {
ctor public Condition(android.net.Uri, String, int);
+ ctor @FlaggedApi("android.app.modes_api") public Condition(@Nullable android.net.Uri, @Nullable String, int, int);
ctor public Condition(android.net.Uri, String, String, String, int, int, int);
+ ctor @FlaggedApi("android.app.modes_api") public Condition(@Nullable android.net.Uri, @Nullable String, @Nullable String, @Nullable String, int, int, int, int);
ctor public Condition(android.os.Parcel);
method public android.service.notification.Condition copy();
method public int describeContents();
method public static boolean isValidId(android.net.Uri, String);
method public static android.net.Uri.Builder newId(android.content.Context);
method public static String relevanceToString(int);
+ method @FlaggedApi("android.app.modes_api") @NonNull public static String sourceToString(int);
method public static String stateToString(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Condition> CREATOR;
field public static final int FLAG_RELEVANT_ALWAYS = 2; // 0x2
field public static final int FLAG_RELEVANT_NOW = 1; // 0x1
field public static final String SCHEME = "condition";
+ field @FlaggedApi("android.app.modes_api") public static final int SOURCE_CONTEXT = 3; // 0x3
+ field @FlaggedApi("android.app.modes_api") public static final int SOURCE_SCHEDULE = 2; // 0x2
+ field @FlaggedApi("android.app.modes_api") public static final int SOURCE_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("android.app.modes_api") public static final int SOURCE_USER_ACTION = 1; // 0x1
field public static final int STATE_ERROR = 3; // 0x3
field public static final int STATE_FALSE = 0; // 0x0
field public static final int STATE_TRUE = 1; // 0x1
@@ -40527,6 +40562,7 @@ package android.service.notification {
field public final android.net.Uri id;
field public final String line1;
field public final String line2;
+ field @FlaggedApi("android.app.modes_api") public final int source;
field public final int state;
field public final String summary;
}
@@ -40695,6 +40731,26 @@ package android.service.notification {
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
}
+ @FlaggedApi("android.app.modes_api") public final class ZenDeviceEffects implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean shouldDimWallpaper();
+ method public boolean shouldDisplayGrayscale();
+ method public boolean shouldSuppressAmbientDisplay();
+ method public boolean shouldUseNightMode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.ZenDeviceEffects> CREATOR;
+ }
+
+ @FlaggedApi("android.app.modes_api") public static final class ZenDeviceEffects.Builder {
+ ctor public ZenDeviceEffects.Builder();
+ ctor public ZenDeviceEffects.Builder(@NonNull android.service.notification.ZenDeviceEffects);
+ method @NonNull public android.service.notification.ZenDeviceEffects build();
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldDimWallpaper(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldDisplayGrayscale(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldSuppressAmbientDisplay(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldUseNightMode(boolean);
+ }
+
public final class ZenPolicy implements android.os.Parcelable {
method public int describeContents();
method public int getPriorityCallSenders();
@@ -43649,6 +43705,7 @@ package android.telephony {
field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array";
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_gcm_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
field public static final String KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT = "iwlan.epdg_address_ip_type_preference_int";
@@ -43664,12 +43721,15 @@ package android.telephony {
field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int";
field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_gcm_key_size_int_array";
field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
field public static final String KEY_PREFIX = "iwlan.";
field public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY = "iwlan.retransmit_timer_sec_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_aead_algorithms_int_array";
field public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_encryption_algorithms_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_aead_algorithms_int_array";
field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
@@ -44693,10 +44753,16 @@ package android.telephony {
method public int getLastCauseCode();
method @Nullable public android.net.LinkProperties getLinkProperties();
method public int getNetworkType();
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
method public int getState();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
+ field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
}
public final class RadioAccessSpecifier implements android.os.Parcelable {
@@ -44879,6 +44945,7 @@ package android.telephony {
field public static final int MMS_ERROR_INVALID_APN = 2; // 0x2
field public static final int MMS_ERROR_INVALID_SUBSCRIPTION_ID = 9; // 0x9
field public static final int MMS_ERROR_IO_ERROR = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.mms_disabled_error") public static final int MMS_ERROR_MMS_DISABLED_BY_CARRIER = 12; // 0xc
field public static final int MMS_ERROR_NO_DATA_NETWORK = 8; // 0x8
field public static final int MMS_ERROR_RETRY = 6; // 0x6
field public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3; // 0x3
@@ -45814,6 +45881,7 @@ package android.telephony.data {
method public int getProxyPort();
method public int getRoamingProtocol();
method public String getUser();
+ method @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public boolean isAlwaysOn();
method public boolean isEnabled();
method public boolean isPersistent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -45853,6 +45921,7 @@ package android.telephony.data {
public static class ApnSetting.Builder {
ctor public ApnSetting.Builder();
method public android.telephony.data.ApnSetting build();
+ method @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") @NonNull public android.telephony.data.ApnSetting.Builder setAlwaysOn(boolean);
method @NonNull public android.telephony.data.ApnSetting.Builder setApnName(@Nullable String);
method @NonNull public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setAuthType(int);
@@ -51975,6 +52044,7 @@ package android.view {
method public android.view.PointerIcon getPointerIcon();
method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
method @Nullable public String[] getReceiveContentMimeTypes();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public float getRequestedFrameRate();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
method public final int getRight();
@@ -52351,6 +52421,7 @@ package android.view {
method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
method public void setPressed(boolean);
method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setRequestedFrameRate(float);
method public final void setRevealOnFocusHint(boolean);
method public final void setRight(int);
method public void setRotation(float);
@@ -52533,6 +52604,11 @@ 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_HIGH = -120.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1.0f;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_X;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_Y;
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1308b1fc578b..f331e7f5fa84 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,40 +1,4 @@
// Baseline format: 1.0
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[]):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[], int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindDouble(int, double):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindInt(int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindLong(int, long):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindNull(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindText(int, String):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnBlob(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnDouble(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnInt(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLength(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLong(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnName(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnText(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnType(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#readColumnBlob(int, byte[], int, int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#step():
- Methods must not throw unchecked exceptions
-
-
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
@@ -425,6 +389,12 @@ 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
+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:
+ Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+
+
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/module-lib-current.txt b/core/api/module-lib-current.txt
index df466ab9b5bb..e7803fbf011d 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -639,3 +639,12 @@ package android.util {
}
+package android.view.accessibility {
+
+ public final class AccessibilityManager {
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean startFlashNotificationSequence(@NonNull android.content.Context, int);
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean stopFlashNotificationSequence(@NonNull android.content.Context);
+ }
+
+}
+
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 5a4be65ef559..285dcc6a3ed9 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -112,6 +112,9 @@ package android.content.pm {
method public abstract boolean setInstantAppCookie(@Nullable byte[]);
}
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x100, 0x200, 0x2000, 0x1000, 0x800, 0x4000, 0x8000, 0x8, 0x10000, 0x20000}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ }
+
public final class SharedLibraryInfo implements android.os.Parcelable {
method public boolean isBuiltin();
method public boolean isDynamic();
@@ -224,6 +227,12 @@ package android.media {
ctor public AudioFormat();
}
+ @Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel {
+ }
+
+ @Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
+ }
+
}
package android.media.tv {
@@ -312,6 +321,9 @@ package android.os {
method public CharSequence getBadgedLabelForUser(CharSequence, android.os.UserHandle);
}
+ @IntDef(flag=true, prefix={"RESTRICTION_"}, value={0x0, 0x1, 0x2, 0x4}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface UserManager.UserRestrictionSource {
+ }
+
}
package android.os.storage {
@@ -487,6 +499,13 @@ package android.telephony {
}
+package android.telephony.euicc {
+
+ @IntDef(prefix={"EUICC_OTA_"}, value={0x1, 0x2, 0x3, 0x4, 0x5}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
+ }
+
+}
+
package android.text.format {
public class DateFormat {
@@ -548,6 +567,9 @@ package android.view {
field public static final int TYPE_STATUS_BAR_PANEL = 2014; // 0x7de
}
+ @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={0x80000, 0x10}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+ }
+
}
package android.view.accessibility {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8d480e5bc8f4..32d252ebda29 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3185,6 +3185,7 @@ package android.companion.virtual {
field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2
field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1
field public static final int LAUNCH_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
}
public static interface VirtualDeviceManager.ActivityListener {
@@ -3330,6 +3331,53 @@ package android.companion.virtual.audio {
}
+package android.companion.virtual.camera {
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCamera implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig getConfig();
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public interface VirtualCameraCallback {
+ method public void onProcessCaptureRequest(int, long, @Nullable android.companion.virtual.camera.VirtualCameraMetadata);
+ method public void onStreamClosed(int);
+ method public void onStreamConfigured(int, @NonNull android.view.Surface, @NonNull android.companion.virtual.camera.VirtualCameraStreamConfig);
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @StringRes public int getDisplayNameStringRes();
+ method @NonNull public java.util.Set<android.companion.virtual.camera.VirtualCameraStreamConfig> getStreamConfigs();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraConfig> CREATOR;
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final class VirtualCameraConfig.Builder {
+ ctor public VirtualCameraConfig.Builder();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder addStreamConfig(int, int, int);
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig build();
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setDisplayNameStringRes(@StringRes int);
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraMetadata 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.companion.virtual.camera.VirtualCameraMetadata> CREATOR;
+ }
+
+ @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraStreamConfig implements android.os.Parcelable {
+ ctor public VirtualCameraStreamConfig(@IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public int describeContents();
+ method public int getFormat();
+ method @IntRange(from=1) public int getHeight();
+ method @IntRange(from=1) public int getWidth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraStreamConfig> CREATOR;
+ }
+
+}
+
package android.companion.virtual.sensor {
public final class VirtualSensor implements android.os.Parcelable {
@@ -3813,11 +3861,15 @@ package android.content.pm {
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
+ public class PackageInfo implements android.os.Parcelable {
+ method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
+ }
+
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3829,6 +3881,7 @@ package android.content.pm {
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
@@ -3885,6 +3938,7 @@ package android.content.pm {
method public void setRequestDowngrade(boolean);
method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
+ method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
}
public class PackageItemInfo {
@@ -3918,7 +3972,7 @@ package android.content.pm {
method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public String getPermissionControllerPackageName();
- method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -3943,13 +3997,13 @@ package android.content.pm {
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
- method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.QUARANTINE_APPS}, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
method @NonNull public boolean shouldShowNewAppInstalledNotification();
method @Deprecated @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
- method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";
@@ -4066,9 +4120,7 @@ package android.content.pm {
public static interface PackageManager.OnPermissionsChangedListener {
method public void onPermissionsChanged(int);
- }
-
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public default void onPermissionsChanged(int, @NonNull String);
}
public static final class PackageManager.UninstallCompleteCallback implements android.os.Parcelable {
@@ -4550,6 +4602,14 @@ package android.hardware.display {
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
+ public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+ }
+
+ public static final class VirtualDisplayConfig.Builder {
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+ }
+
}
package android.hardware.hdmi {
@@ -4564,7 +4624,7 @@ package android.hardware.hdmi {
}
public static interface HdmiClient.OnDeviceSelectedListener {
- method public void onDeviceSelected(@android.hardware.hdmi.HdmiControlManager.ControlCallbackResult int, int);
+ method public void onDeviceSelected(int, int);
}
public final class HdmiControlManager {
@@ -4762,9 +4822,6 @@ package android.hardware.hdmi {
method public void onChange(@NonNull String);
}
- @IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface HdmiControlManager.ControlCallbackResult {
- }
-
public static interface HdmiControlManager.HotplugEventListener {
method public void onReceived(android.hardware.hdmi.HdmiHotplugEvent);
}
@@ -4911,7 +4968,7 @@ package android.hardware.hdmi {
}
public static interface HdmiSwitchClient.OnSelectListener {
- method public void onSelect(@android.hardware.hdmi.HdmiControlManager.ControlCallbackResult int);
+ method public void onSelect(int);
}
public class HdmiTimerRecordSources {
@@ -5628,14 +5685,14 @@ package android.hardware.radio {
}
public final class ProgramSelector implements android.os.Parcelable {
- ctor public ProgramSelector(@android.hardware.radio.ProgramSelector.ProgramType int, @NonNull android.hardware.radio.ProgramSelector.Identifier, @Nullable android.hardware.radio.ProgramSelector.Identifier[], @Nullable long[]);
- method @NonNull public static android.hardware.radio.ProgramSelector createAmFmSelector(@android.hardware.radio.RadioManager.Band int, int);
- method @NonNull public static android.hardware.radio.ProgramSelector createAmFmSelector(@android.hardware.radio.RadioManager.Band int, int, int);
+ ctor public ProgramSelector(int, @NonNull android.hardware.radio.ProgramSelector.Identifier, @Nullable android.hardware.radio.ProgramSelector.Identifier[], @Nullable long[]);
+ method @NonNull public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int);
+ method @NonNull public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int, int);
method public int describeContents();
- method @NonNull public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(@android.hardware.radio.ProgramSelector.IdentifierType int);
- method public long getFirstId(@android.hardware.radio.ProgramSelector.IdentifierType int);
+ method @NonNull public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int);
+ method public long getFirstId(int);
method @NonNull public android.hardware.radio.ProgramSelector.Identifier getPrimaryId();
- method @Deprecated @android.hardware.radio.ProgramSelector.ProgramType public int getProgramType();
+ method @Deprecated public int getProgramType();
method @NonNull public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
method @Deprecated @NonNull public long[] getVendorIds();
method @NonNull public android.hardware.radio.ProgramSelector withSecondaryPreferred(@NonNull android.hardware.radio.ProgramSelector.Identifier);
@@ -5684,21 +5741,15 @@ package android.hardware.radio {
}
public static final class ProgramSelector.Identifier implements android.os.Parcelable {
- ctor public ProgramSelector.Identifier(@android.hardware.radio.ProgramSelector.IdentifierType int, long);
+ ctor public ProgramSelector.Identifier(int, long);
method public int describeContents();
- method @android.hardware.radio.ProgramSelector.IdentifierType public int getType();
+ method public int getType();
method public long getValue();
method public boolean isCategoryType();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR;
}
- @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_LOCATION}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType {
- }
-
- @Deprecated @IntDef(prefix={"PROGRAM_TYPE_"}, value={android.hardware.radio.ProgramSelector.PROGRAM_TYPE_INVALID, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DAB, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DRMO, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_SXM}) @IntRange(from=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.ProgramType {
- }
-
public class RadioManager {
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener);
@@ -5756,9 +5807,6 @@ package android.hardware.radio {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.AmBandDescriptor> CREATOR;
}
- @IntDef(prefix={"BAND_"}, value={android.hardware.radio.RadioManager.BAND_INVALID, android.hardware.radio.RadioManager.BAND_AM, android.hardware.radio.RadioManager.BAND_FM, android.hardware.radio.RadioManager.BAND_AM_HD, android.hardware.radio.RadioManager.BAND_FM_HD}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RadioManager.Band {
- }
-
public static class RadioManager.BandConfig implements android.os.Parcelable {
method public int describeContents();
method public int getLowerLimit();
@@ -5829,8 +5877,8 @@ package android.hardware.radio {
method public boolean isBackgroundScanningSupported();
method public boolean isCaptureSupported();
method public boolean isInitializationRequired();
- method public boolean isProgramIdentifierSupported(@android.hardware.radio.ProgramSelector.IdentifierType int);
- method public boolean isProgramTypeSupported(@android.hardware.radio.ProgramSelector.ProgramType int);
+ method public boolean isProgramIdentifierSupported(int);
+ method public boolean isProgramTypeSupported(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ModuleProperties> CREATOR;
}
@@ -10514,7 +10562,7 @@ package android.os {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
- method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int getUserSwitchability();
method @NonNull @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public java.util.Set<android.os.UserHandle> getVisibleUsers();
@@ -10574,14 +10622,11 @@ package android.os {
public static final class UserManager.EnforcingUser implements android.os.Parcelable {
method public int describeContents();
method public android.os.UserHandle getUserHandle();
- method @android.os.UserManager.UserRestrictionSource public int getUserRestrictionSource();
+ method public int getUserRestrictionSource();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR;
}
- @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.os.UserManager.RESTRICTION_NOT_SET, android.os.UserManager.RESTRICTION_SOURCE_SYSTEM, android.os.UserManager.RESTRICTION_SOURCE_DEVICE_OWNER, android.os.UserManager.RESTRICTION_SOURCE_PROFILE_OWNER}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface UserManager.UserRestrictionSource {
- }
-
public abstract class Vibrator {
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
@@ -11234,15 +11279,11 @@ package android.provider {
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field @Deprecated public static final String MTU = "mtu";
- field public static final String MTU_V4 = "mtu_v4";
- field public static final String MTU_V6 = "mtu_v6";
field public static final int NO_APN_SET_ID = 0; // 0x0
field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
field public static final int UNEDITED = 0; // 0x0
field public static final int USER_DELETED = 2; // 0x2
- field public static final String USER_EDITABLE = "user_editable";
field public static final int USER_EDITED = 1; // 0x1
- field public static final String USER_VISIBLE = "user_visible";
field public static final String WAIT_TIME_RETRY = "wait_time";
}
@@ -11889,13 +11930,13 @@ package android.service.euicc {
method public android.service.carrier.CarrierIdentifier getCarrierIdentifier();
method public String getIccid();
method @Nullable public String getNickname();
- method @android.service.euicc.EuiccProfileInfo.PolicyRule public int getPolicyRules();
- method @android.service.euicc.EuiccProfileInfo.ProfileClass public int getProfileClass();
+ method public int getPolicyRules();
+ method public int getProfileClass();
method public String getProfileName();
method public String getServiceProviderName();
- method @android.service.euicc.EuiccProfileInfo.ProfileState public int getState();
+ method public int getState();
method @Nullable public java.util.List<android.telephony.UiccAccessRule> getUiccAccessRules();
- method public boolean hasPolicyRule(@android.service.euicc.EuiccProfileInfo.PolicyRule int);
+ method public boolean hasPolicyRule(int);
method public boolean hasPolicyRules();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.euicc.EuiccProfileInfo> CREATOR;
@@ -11916,23 +11957,14 @@ package android.service.euicc {
method public android.service.euicc.EuiccProfileInfo.Builder setCarrierIdentifier(android.service.carrier.CarrierIdentifier);
method public android.service.euicc.EuiccProfileInfo.Builder setIccid(String);
method public android.service.euicc.EuiccProfileInfo.Builder setNickname(String);
- method public android.service.euicc.EuiccProfileInfo.Builder setPolicyRules(@android.service.euicc.EuiccProfileInfo.PolicyRule int);
- method public android.service.euicc.EuiccProfileInfo.Builder setProfileClass(@android.service.euicc.EuiccProfileInfo.ProfileClass int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setPolicyRules(int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setProfileClass(int);
method public android.service.euicc.EuiccProfileInfo.Builder setProfileName(String);
method public android.service.euicc.EuiccProfileInfo.Builder setServiceProviderName(String);
- method public android.service.euicc.EuiccProfileInfo.Builder setState(@android.service.euicc.EuiccProfileInfo.ProfileState int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setState(int);
method public android.service.euicc.EuiccProfileInfo.Builder setUiccAccessRule(@Nullable java.util.List<android.telephony.UiccAccessRule>);
}
- @IntDef(flag=true, prefix={"POLICY_RULE_"}, value={android.service.euicc.EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, android.service.euicc.EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, android.service.euicc.EuiccProfileInfo.POLICY_RULE_DELETE_AFTER_DISABLING}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.PolicyRule {
- }
-
- @IntDef(prefix={"PROFILE_CLASS_"}, value={android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_TESTING, android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_PROVISIONING, android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL, 0xffffffff}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.ProfileClass {
- }
-
- @IntDef(prefix={"PROFILE_STATE_"}, value={android.service.euicc.EuiccProfileInfo.PROFILE_STATE_DISABLED, android.service.euicc.EuiccProfileInfo.PROFILE_STATE_ENABLED, 0xffffffff}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.ProfileState {
- }
-
public abstract class EuiccService extends android.app.Service {
ctor public EuiccService();
method public void dump(@NonNull java.io.PrintWriter);
@@ -11943,14 +11975,14 @@ package android.service.euicc {
method @NonNull public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @NonNull android.os.Bundle);
method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
method @Deprecated public abstract int onEraseSubscriptions(int);
- method public int onEraseSubscriptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
+ method public int onEraseSubscriptions(int, int);
method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
method @NonNull public android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean);
method public abstract String onGetEid(int);
method @NonNull public abstract android.telephony.euicc.EuiccInfo onGetEuiccInfo(int);
method @NonNull public abstract android.service.euicc.GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int);
- method @android.telephony.euicc.EuiccManager.OtaStatus public abstract int onGetOtaStatus(int);
+ method public abstract int onGetOtaStatus(int);
method public abstract int onRetainSubscriptionsForFactoryReset(int);
method public abstract void onStartOtaIfNecessary(int, android.service.euicc.EuiccService.OtaStatusChangedCallback);
method @Deprecated public abstract int onSwitchToSubscription(int, @Nullable String, boolean);
@@ -12214,7 +12246,7 @@ package android.service.persistentdata {
public class PersistentDataBlockManager {
method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize();
- method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
@@ -12227,9 +12259,6 @@ package android.service.persistentdata {
field public static final int FLASH_LOCK_UNLOCKED = 0; // 0x0
}
- @IntDef(prefix={"FLASH_LOCK_"}, value={android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_UNKNOWN, android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_LOCKED, android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_UNLOCKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PersistentDataBlockManager.FlashLockState {
- }
-
}
package android.service.quicksettings {
@@ -14793,6 +14822,7 @@ package android.telephony.data {
method @Deprecated public int getMtu();
method public int getMtuV4();
method public int getMtuV6();
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
method public int getPduSessionId();
method public int getProtocolType();
@@ -14829,6 +14859,7 @@ package android.telephony.data {
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -14908,6 +14939,7 @@ package android.telephony.data {
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -14969,6 +15001,7 @@ package android.telephony.data {
method public final int getSlotIndex();
method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
@@ -15017,10 +15050,10 @@ package android.telephony.euicc {
public class EuiccCardManager {
method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void cancelSession(String, byte[], int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void listNotifications(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
@@ -15033,9 +15066,9 @@ package android.telephony.euicc {
method public void requestProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
method public void requestRulesAuthTable(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccRulesAuthTable>);
method public void requestSmdsAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
- method public void resetMemory(String, @android.telephony.euicc.EuiccCardManager.ResetOption int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void resetMemory(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void retrieveNotification(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification>);
- method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void retrieveNotificationList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method @Deprecated public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
@@ -15055,12 +15088,6 @@ package android.telephony.euicc {
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
- @IntDef(prefix={"CANCEL_REASON_"}, value={android.telephony.euicc.EuiccCardManager.CANCEL_REASON_END_USER_REJECTED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_POSTPONED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_TIMEOUT, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_PPR_NOT_ALLOWED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.CancelReason {
- }
-
- @IntDef(flag=true, prefix={"RESET_OPTION_"}, value={android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.ResetOption {
- }
-
public static interface EuiccCardManager.ResultCallback<T> {
method public void onComplete(int, T);
}
@@ -15068,7 +15095,7 @@ package android.telephony.euicc {
public class EuiccManager {
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(int, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
@@ -15103,18 +15130,15 @@ package android.telephony.euicc {
field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
}
- @IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
- }
-
public final class EuiccNotification implements android.os.Parcelable {
- ctor public EuiccNotification(int, String, @android.telephony.euicc.EuiccNotification.Event int, @Nullable byte[]);
+ ctor public EuiccNotification(int, String, int, @Nullable byte[]);
method public int describeContents();
method @Nullable public byte[] getData();
- method @android.telephony.euicc.EuiccNotification.Event public int getEvent();
+ method public int getEvent();
method public int getSeq();
method public String getTargetAddr();
method public void writeToParcel(android.os.Parcel, int);
- field @android.telephony.euicc.EuiccNotification.Event public static final int ALL_EVENTS = 15; // 0xf
+ field public static final int ALL_EVENTS = 15; // 0xf
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccNotification> CREATOR;
field public static final int EVENT_DELETE = 8; // 0x8
field public static final int EVENT_DISABLE = 4; // 0x4
@@ -15122,13 +15146,10 @@ package android.telephony.euicc {
field public static final int EVENT_INSTALL = 1; // 0x1
}
- @IntDef(flag=true, prefix={"EVENT_"}, value={android.telephony.euicc.EuiccNotification.EVENT_INSTALL, android.telephony.euicc.EuiccNotification.EVENT_ENABLE, android.telephony.euicc.EuiccNotification.EVENT_DISABLE, android.telephony.euicc.EuiccNotification.EVENT_DELETE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccNotification.Event {
- }
-
public final class EuiccRulesAuthTable implements android.os.Parcelable {
method public int describeContents();
- method public int findIndex(@android.service.euicc.EuiccProfileInfo.PolicyRule int, android.service.carrier.CarrierIdentifier);
- method public boolean hasPolicyRuleFlag(int, @android.telephony.euicc.EuiccRulesAuthTable.PolicyRuleFlag int);
+ method public int findIndex(int, android.service.carrier.CarrierIdentifier);
+ method public boolean hasPolicyRuleFlag(int, int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccRulesAuthTable> CREATOR;
field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1
@@ -15140,9 +15161,6 @@ package android.telephony.euicc {
method public android.telephony.euicc.EuiccRulesAuthTable build();
}
- @IntDef(flag=true, prefix={"POLICY_RULE_FLAG_"}, value={android.telephony.euicc.EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccRulesAuthTable.PolicyRuleFlag {
- }
-
}
package android.telephony.gba {
@@ -16834,6 +16852,10 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteCapabilities> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteCapabilitiesCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
@@ -16852,6 +16874,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -16872,6 +16895,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -17036,7 +17060,7 @@ package android.view {
}
public abstract class Window {
- method public void addSystemFlags(@android.view.WindowManager.LayoutParams.SystemFlags int);
+ method public void addSystemFlags(int);
}
public interface WindowManager extends android.view.ViewManager {
@@ -17055,9 +17079,6 @@ package android.view {
field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
}
- @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
- }
-
}
package android.view.accessibility {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 865240207a95..dec1ee52712d 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -517,6 +517,18 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
+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:
+ Invalid nullability on parameter `name` in method `getSystemService`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.voice.VisualQueryDetectionService#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.service.voice.VisualQueryDetectionService#openFileInput(String):
+ Invalid nullability on method `openFileInput` return. Overrides of unannotated super method cannot be Nullable.
+InvalidNullabilityOverride: android.service.voice.VisualQueryDetectionService#openFileInput(String) parameter #0:
+ Invalid nullability on parameter `filename` in method `openFileInput`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+
+
KotlinKeyword: android.app.Notification#when:
Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 402a7184c7c2..51b8a11e1791 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -112,6 +112,22 @@ package android.hardware.hdmi {
method @Deprecated public void requestRemoteDeviceToBecomeActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
}
+ @IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HdmiControlManager.ControlCallbackResult {
+ }
+
+}
+
+package android.hardware.radio {
+
+ @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_LOCATION}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType {
+ }
+
+ @Deprecated @IntDef(prefix={"PROGRAM_TYPE_"}, value={android.hardware.radio.ProgramSelector.PROGRAM_TYPE_INVALID, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DAB, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DRMO, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_SXM}) @IntRange(from=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.ProgramType {
+ }
+
+ @IntDef(prefix={"BAND_"}, value={android.hardware.radio.RadioManager.BAND_INVALID, android.hardware.radio.RadioManager.BAND_AM, android.hardware.radio.RadioManager.BAND_FM, android.hardware.radio.RadioManager.BAND_AM_HD, android.hardware.radio.RadioManager.BAND_FM_HD}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RadioManager.Band {
+ }
+
}
package android.media.tv {
@@ -145,6 +161,19 @@ package android.os {
}
+package android.service.euicc {
+
+ @IntDef(flag=true, prefix={"POLICY_RULE_"}, value={android.service.euicc.EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, android.service.euicc.EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, android.service.euicc.EuiccProfileInfo.POLICY_RULE_DELETE_AFTER_DISABLING}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.PolicyRule {
+ }
+
+ @IntDef(prefix={"PROFILE_CLASS_"}, value={android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_TESTING, android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_PROVISIONING, android.service.euicc.EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL, 0xffffffff}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.ProfileClass {
+ }
+
+ @IntDef(prefix={"PROFILE_STATE_"}, value={android.service.euicc.EuiccProfileInfo.PROFILE_STATE_DISABLED, android.service.euicc.EuiccProfileInfo.PROFILE_STATE_ENABLED, 0xffffffff}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccProfileInfo.ProfileState {
+ }
+
+}
+
package android.service.notification {
public abstract class NotificationListenerService extends android.app.Service {
@@ -165,6 +194,13 @@ package android.service.notification {
}
+package android.service.persistentdata {
+
+ @IntDef(prefix={"FLASH_LOCK_"}, value={android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_UNKNOWN, android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_LOCKED, android.service.persistentdata.PersistentDataBlockManager.FLASH_LOCK_UNLOCKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PersistentDataBlockManager.FlashLockState {
+ }
+
+}
+
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -213,6 +249,22 @@ package android.telephony.data {
}
+package android.telephony.euicc {
+
+ @IntDef(prefix={"CANCEL_REASON_"}, value={android.telephony.euicc.EuiccCardManager.CANCEL_REASON_END_USER_REJECTED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_POSTPONED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_TIMEOUT, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_PPR_NOT_ALLOWED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.CancelReason {
+ }
+
+ @IntDef(flag=true, prefix={"RESET_OPTION_"}, value={android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.ResetOption {
+ }
+
+ @IntDef(flag=true, prefix={"EVENT_"}, value={android.telephony.euicc.EuiccNotification.EVENT_INSTALL, android.telephony.euicc.EuiccNotification.EVENT_ENABLE, android.telephony.euicc.EuiccNotification.EVENT_DISABLE, android.telephony.euicc.EuiccNotification.EVENT_DELETE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccNotification.Event {
+ }
+
+ @IntDef(flag=true, prefix={"POLICY_RULE_FLAG_"}, value={android.telephony.euicc.EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccRulesAuthTable.PolicyRuleFlag {
+ }
+
+}
+
package android.telephony.ims {
public interface DelegateStateCallback {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8b20720418b6..93932e4944ad 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -541,7 +541,6 @@ package android.app.admin {
field public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
field public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
field public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
- field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
}
public class DevicePolicyManager {
@@ -3604,6 +3603,8 @@ package android.view.accessibility {
public final class AccessibilityManager {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
method public boolean hasAnyDirectConnection();
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean startFlashNotificationSequence(@NonNull android.content.Context, int);
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean stopFlashNotificationSequence(@NonNull android.content.Context);
}
public class AccessibilityNodeInfo implements android.os.Parcelable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 3a91e25de9e6..bf26bd0a0ec6 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -511,6 +511,16 @@ 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
+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:
+ Invalid nullability on parameter `configuration` in method `onConfigurationChanged`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.window.WindowProviderService#registerComponentCallbacks(android.content.ComponentCallbacks) parameter #0:
+ Invalid nullability on parameter `callback` in method `registerComponentCallbacks`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.window.WindowProviderService#unregisterComponentCallbacks(android.content.ComponentCallbacks) parameter #0:
+ Invalid nullability on parameter `callback` in method `unregisterComponentCallbacks`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+
+
KotlinKeyword: android.app.Notification#when:
Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 48cafc596d87..dfe3344a466a 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -413,6 +413,10 @@ aidl_interface {
backend: {
rust: {
enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
},
},
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3f9cc65ba1db..8ad6ea207665 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -632,6 +632,7 @@ public class AccessibilityServiceInfo implements Parcelable {
InputDevice.SOURCE_JOYSTICK,
InputDevice.SOURCE_SENSOR
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface MotionEventSources {}
/**
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index af00f316de7b..6ec956ee7783 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -24,6 +24,8 @@ import android.util.ArrayMap;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityInteractionClient;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
@@ -92,6 +94,7 @@ public final class TouchInteractionController {
STATE_DRAGGING,
STATE_DELEGATING
})
+ @Retention(RetentionPolicy.SOURCE)
private @interface State {}
// The maximum number of pointers that can be touching the screen at once. (See MAX_POINTER_ID
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index be433d2ab0f2..bb335fae887b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -23,7 +23,9 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import static android.os.Process.myUid;
+
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+
import static java.lang.Character.MIN_VALUE;
import android.annotation.AnimRes;
@@ -1000,6 +1002,7 @@ public class Activity extends ContextThemeWrapper
FULLSCREEN_MODE_REQUEST_EXIT,
FULLSCREEN_MODE_REQUEST_ENTER
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FullscreenModeRequest {}
/** Request type of {@link #requestFullscreenMode(int, OutcomeReceiver)}, to request exiting the
@@ -1016,6 +1019,7 @@ public class Activity extends ContextThemeWrapper
OVERRIDE_TRANSITION_OPEN,
OVERRIDE_TRANSITION_CLOSE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface OverrideTransition {}
/**
@@ -6348,6 +6352,10 @@ public class Activity extends ContextThemeWrapper
*/
public boolean startActivityIfNeeded(@RequiresPermission @NonNull Intent intent,
int requestCode, @Nullable Bundle options) {
+ if (Instrumentation.DEBUG_START_ACTIVITY) {
+ Log.d("Instrumentation", "startActivity: intent=" + intent
+ + " requestCode=" + requestCode + " options=" + options, new Throwable());
+ }
if (mParent == null) {
int result = ActivityManager.START_RETURN_INTENT_TO_CALLER;
try {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8b4ebaee04c5..854e12177fb5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4998,6 +4998,7 @@ public class ActivityManager {
STOP_USER_ON_SWITCH_TRUE,
STOP_USER_ON_SWITCH_FALSE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface StopUserOnSwitch {}
/**
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 26f1c4b146a5..9c279c3b8254 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -2563,11 +2563,14 @@ public class ActivityOptions extends ComponentOptions {
public static final int TYPE_LOCKSCREEN = 3;
/** Launched from recents gesture handler. */
public static final int TYPE_RECENTS_ANIMATION = 4;
+ /** Launched from desktop's transition handler. */
+ public static final int TYPE_DESKTOP_ANIMATION = 5;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_LAUNCHER,
TYPE_NOTIFICATION,
TYPE_LOCKSCREEN,
+ TYPE_DESKTOP_ANIMATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface SourceType {}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c136db68fd25..02eaf0b3bbd3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -56,7 +56,7 @@ import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupAnnotations.OperationType;
import android.app.compat.CompatChanges;
import android.app.sdksandbox.sandboxactivity.ActivityContextInfo;
-import android.app.sdksandbox.sandboxactivity.ActivityContextInfoProvider;
+import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -3795,8 +3795,10 @@ public final class ActivityThread extends ClientTransactionHandler
r.activityInfo.targetActivity);
}
- boolean isSandboxActivityContext = sandboxActivitySdkBasedContext()
- && r.intent.isSandboxActivity(mSystemContext);
+ boolean isSandboxActivityContext =
+ sandboxActivitySdkBasedContext()
+ && SdkSandboxActivityAuthority.isSdkSandboxActivity(
+ mSystemContext, r.intent);
boolean isSandboxedSdkContextUsed = false;
ContextImpl activityBaseContext;
if (isSandboxActivityContext) {
@@ -4041,11 +4043,12 @@ public final class ActivityThread extends ClientTransactionHandler
*/
@Nullable
private ContextImpl createBaseContextForSandboxActivity(@NonNull ActivityClientRecord r) {
- ActivityContextInfoProvider contextInfoProvider = ActivityContextInfoProvider.getInstance();
+ SdkSandboxActivityAuthority sdkSandboxActivityAuthority =
+ SdkSandboxActivityAuthority.getInstance();
ActivityContextInfo contextInfo;
try {
- contextInfo = contextInfoProvider.getActivityContextInfo(r.intent);
+ contextInfo = sdkSandboxActivityAuthority.getActivityContextInfo(r.intent);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Passed intent does not match an expected sandbox activity", e);
return null;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b74b075c9f62..c5e132fc0c4b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -9927,10 +9927,11 @@ public class AppOpsManager {
if (i != firstInteresting) {
sb.append('\n');
}
- if (!sFullLog && sb.length() + trace[i].toString().length() > 600) {
+ final String traceString = trace[i].toString();
+ if (!sFullLog && sb.length() + traceString.length() > 600) {
break;
}
- sb.append(trace[i]);
+ sb.append(traceString);
}
return sb.toString();
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 919e084002ea..d93544972e7a 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -22,12 +22,12 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.NotificationManager.InterruptionFilter;
-import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import android.view.WindowInsetsController;
@@ -36,7 +36,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * Rule instance information for zen mode.
+ * Rule instance information for a zen (aka DND or Attention Management) mode.
*/
public final class AutomaticZenRule implements Parcelable {
/* @hide */
@@ -45,7 +45,9 @@ public final class AutomaticZenRule implements Parcelable {
private static final int DISABLED = 0;
/**
- * Rule is of an unknown type. This is the default value if not provided by the owning app.
+ * Rule is of an unknown type. This is the default value if not provided by the owning app,
+ * and the value returned if the true type was added in an API level lower than the calling
+ * app's targetSdk.
*/
@FlaggedApi(Flags.FLAG_MODES_API)
public static final int TYPE_UNKNOWN = -1;
@@ -111,6 +113,7 @@ public final class AutomaticZenRule implements Parcelable {
private ComponentName configurationActivity;
private long creationTime;
private ZenPolicy mZenPolicy;
+ private ZenDeviceEffects mDeviceEffects;
private boolean mModified = false;
private String mPkg;
private int mType = TYPE_UNKNOWN;
@@ -190,6 +193,7 @@ public final class AutomaticZenRule implements Parcelable {
/**
* @hide
*/
+ // TODO: b/310620812 - Remove when the flag is inlined (all system callers should use Builder).
public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled,
long creationTime) {
@@ -209,10 +213,11 @@ public final class AutomaticZenRule implements Parcelable {
configurationActivity = getTrimmedComponentName(
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
- mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
+ mZenPolicy = source.readParcelable(null, ZenPolicy.class);
mModified = source.readInt() == ENABLED;
mPkg = source.readString();
if (Flags.modesApi()) {
+ mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
@@ -274,10 +279,18 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Gets the zen policy.
*/
+ @Nullable
public ZenPolicy getZenPolicy() {
return mZenPolicy == null ? null : this.mZenPolicy.copy();
}
+ /** Gets the {@link ZenDeviceEffects} of this rule. */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public ZenDeviceEffects getDeviceEffects() {
+ return mDeviceEffects;
+ }
+
/**
* Returns the time this rule was created, represented as milliseconds since the epoch.
*/
@@ -325,11 +338,21 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Sets the zen policy.
*/
- public void setZenPolicy(ZenPolicy zenPolicy) {
+ public void setZenPolicy(@Nullable ZenPolicy zenPolicy) {
this.mZenPolicy = (zenPolicy == null ? null : zenPolicy.copy());
}
/**
+ * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes to
+ * the device behavior that should apply while the rule is active, but are not directly related
+ * to suppressing notifications (for example: disabling always-on display).
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
+ mDeviceEffects = deviceEffects;
+ }
+
+ /**
* Sets the configuration activity - an activity that handles
* {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
* about this rule and/or allows them to configure it. This is required to be non-null for rules
@@ -357,7 +380,7 @@ public final class AutomaticZenRule implements Parcelable {
* Gets the type of the rule.
*/
@FlaggedApi(Flags.FLAG_MODES_API)
- public int getType() {
+ public @Type int getType() {
return mType;
}
@@ -451,6 +474,7 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeInt(mModified ? ENABLED : DISABLED);
dest.writeString(mPkg);
if (Flags.modesApi()) {
+ dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
dest.writeString(mTriggerDescription);
@@ -472,7 +496,8 @@ public final class AutomaticZenRule implements Parcelable {
.append(",mZenPolicy=").append(mZenPolicy);
if (Flags.modesApi()) {
- sb.append(",allowManualInvocation=").append(mAllowManualInvocation)
+ sb.append(",deviceEffects=").append(mDeviceEffects)
+ .append(",allowManualInvocation=").append(mAllowManualInvocation)
.append(",iconResId=").append(mIconResId)
.append(",triggerDescription=").append(mTriggerDescription)
.append(",type=").append(mType);
@@ -498,6 +523,7 @@ public final class AutomaticZenRule implements Parcelable {
&& other.creationTime == creationTime;
if (Flags.modesApi()) {
return finalEquals
+ && Objects.equals(other.mDeviceEffects, mDeviceEffects)
&& other.mAllowManualInvocation == mAllowManualInvocation
&& other.mIconResId == mIconResId
&& Objects.equals(other.mTriggerDescription, mTriggerDescription)
@@ -510,8 +536,8 @@ public final class AutomaticZenRule implements Parcelable {
public int hashCode() {
if (Flags.modesApi()) {
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
- configurationActivity, mZenPolicy, mModified, creationTime, mPkg,
- mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
+ configurationActivity, mZenPolicy, mDeviceEffects, mModified, creationTime,
+ mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
}
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
@@ -570,9 +596,10 @@ public final class AutomaticZenRule implements Parcelable {
private ComponentName mOwner;
private Uri mConditionId;
private int mInterruptionFilter;
- private boolean mEnabled;
+ private boolean mEnabled = true;
private ComponentName mConfigurationActivity = null;
private ZenPolicy mPolicy = null;
+ private ZenDeviceEffects mDeviceEffects = null;
private int mType;
private String mDescription;
private int mIconResId;
@@ -588,6 +615,7 @@ public final class AutomaticZenRule implements Parcelable {
mEnabled = rule.isEnabled();
mConfigurationActivity = rule.getConfigurationActivity();
mPolicy = rule.getZenPolicy();
+ mDeviceEffects = rule.getDeviceEffects();
mType = rule.getType();
mDescription = rule.getTriggerDescription();
mIconResId = rule.getIconResId();
@@ -601,44 +629,80 @@ public final class AutomaticZenRule implements Parcelable {
mConditionId = conditionId;
}
+ /**
+ * Sets the name of this rule.
+ */
public @NonNull Builder setName(@NonNull String name) {
mName = name;
return this;
}
+ /**
+ * Sets the component (service or activity) that owns this rule.
+ */
public @NonNull Builder setOwner(@Nullable ComponentName owner) {
mOwner = owner;
return this;
}
+ /**
+ * Sets the representation of the state that causes this rule to become active.
+ */
public @NonNull Builder setConditionId(@NonNull Uri conditionId) {
mConditionId = conditionId;
return this;
}
+ /**
+ * Sets the interruption filter that is applied when this rule is active.
+ */
public @NonNull Builder setInterruptionFilter(
@InterruptionFilter int interruptionFilter) {
mInterruptionFilter = interruptionFilter;
return this;
}
+ /**
+ * Enables this rule. Rules are enabled by default.
+ */
public @NonNull Builder setEnabled(boolean enabled) {
mEnabled = enabled;
return this;
}
+ /**
+ * Sets the configuration activity - an activity that handles
+ * {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more
+ * information about this rule and/or allows them to configure it. This is required to be
+ * non-null for rules that are not backed by a
+ * {@link android.service.notification.ConditionProviderService}.
+ */
public @NonNull Builder setConfigurationActivity(
@Nullable ComponentName configurationActivity) {
mConfigurationActivity = configurationActivity;
return this;
}
+ /**
+ * Sets the zen policy.
+ */
public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) {
mPolicy = policy;
return this;
}
/**
+ * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes
+ * to the device behavior that should apply while the rule is active, but are not directly
+ * related to suppressing notifications (for example: disabling always-on display).
+ */
+ @NonNull
+ public Builder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
+ mDeviceEffects = deviceEffects;
+ return this;
+ }
+
+ /**
* Sets the type of the rule
*/
public @NonNull Builder setType(@Type int type) {
@@ -687,6 +751,7 @@ public final class AutomaticZenRule implements Parcelable {
public @NonNull AutomaticZenRule build() {
AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
mConditionId, mPolicy, mInterruptionFilter, mEnabled);
+ rule.mDeviceEffects = mDeviceEffects;
rule.creationTime = mCreationTime;
rule.mType = mType;
rule.mTriggerDescription = mDescription;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 08c18c8b7448..4f8e8dd813a1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3482,7 +3482,8 @@ class ContextImpl extends Context {
mResources = r;
// only do this if the user already has more than one preferred locale
- if (r.getConfiguration().getLocales().size() > 1) {
+ if (android.content.res.Flags.defaultLocale()
+ && r.getConfiguration().getLocales().size() > 1) {
LocaleConfig lc = getUserId() < 0
? LocaleConfig.fromContextIgnoringOverride(this)
: new LocaleConfig(this);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 355092378279..fd13174a6a39 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -575,8 +575,9 @@ public class DownloadManager {
extras.putString(Downloads.DIR_TYPE, dirType);
client.call(Downloads.CALL_CREATE_EXTERNAL_PUBLIC_DIR, null, extras);
} catch (RemoteException e) {
- throw new IllegalStateException("Unable to create directory: "
- + file.getAbsolutePath());
+ throw new IllegalStateException(
+ "Unable to create directory: " + file.getAbsolutePath(),
+ e);
}
} else {
if (file.exists()) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ec5effd0963d..94385717d349 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -214,6 +214,8 @@ interface INotificationManager
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
AutomaticZenRule getAutomaticZenRule(String id);
+ Map<String, AutomaticZenRule> getAutomaticZenRules();
+ // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
List<ZenModeConfig.ZenRule> getZenRules();
String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 357ee0a89697..2162e3a77f15 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -43,6 +43,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.TestLooperManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -67,6 +68,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.StringJoiner;
import java.util.concurrent.TimeoutException;
/**
@@ -100,6 +102,10 @@ public class Instrumentation {
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ // If set, will print the stack trace for activity starts within the process
+ static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+ SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
+
/**
* @hide
*/
@@ -577,6 +583,9 @@ public class Instrumentation {
*/
@NonNull
public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: intent=" + intent + " options=" + options, new Throwable());
+ }
validateNotAppThread();
final Activity activity;
@@ -1891,6 +1900,10 @@ public class Instrumentation {
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: who=" + who + " source=" + target + " intent=" + intent
+ + " requestCode=" + requestCode + " options=" + options, new Throwable());
+ }
Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
@@ -1971,6 +1984,14 @@ public class Instrumentation {
public int execStartActivitiesAsUser(Context who, IBinder contextThread,
IBinder token, Activity target, Intent[] intents, Bundle options,
int userId) {
+ if (DEBUG_START_ACTIVITY) {
+ StringJoiner joiner = new StringJoiner(", ");
+ for (Intent i : intents) {
+ joiner.add(i.toString());
+ }
+ Log.d(TAG, "startActivities: who=" + who + " source=" + target + " userId=" + userId
+ + " intents=[" + joiner + "] options=" + options, new Throwable());
+ }
Objects.requireNonNull(intents);
for (int i = intents.length - 1; i >= 0; i--) {
Objects.requireNonNull(intents[i]);
@@ -2055,6 +2076,11 @@ public class Instrumentation {
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: who=" + who + " target=" + target
+ + " intent=" + intent + " requestCode=" + requestCode
+ + " options=" + options, new Throwable());
+ }
Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
@@ -2130,6 +2156,11 @@ public class Instrumentation {
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String resultWho,
Intent intent, int requestCode, Bundle options, UserHandle user) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: who=" + who + " user=" + user + " intent=" + intent
+ + " requestCode=" + requestCode + " resultWho=" + resultWho
+ + " options=" + options, new Throwable());
+ }
Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
@@ -2184,6 +2215,12 @@ public class Instrumentation {
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options,
boolean ignoreTargetSecurity, int userId) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: who=" + who + " source=" + target + " userId=" + userId
+ + " intent=" + intent + " requestCode=" + requestCode
+ + " ignoreTargetSecurity=" + ignoreTargetSecurity + " options=" + options,
+ new Throwable());
+ }
Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
@@ -2239,6 +2276,10 @@ public class Instrumentation {
public void execStartActivityFromAppTask(
Context who, IBinder contextThread, IAppTask appTask,
Intent intent, Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(TAG, "startActivity: who=" + who + " intent=" + intent
+ + " options=" + options, new Throwable());
+ }
Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 545ba8e81f62..6aad1682466d 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -64,6 +64,8 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;
import com.android.internal.widget.VerifyCredentialResponse;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
@@ -235,6 +237,7 @@ public class KeyguardManager {
PIN,
PATTERN
})
+ @Retention(RetentionPolicy.SOURCE)
@interface LockTypes {}
private final IKeyguardLockedStateListener mIKeyguardLockedStateListener =
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2d80b1ffca6c..337e3f1195be 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -924,6 +924,7 @@ public class Notification implements Parcelable
VISIBILITY_SECRET,
NotificationManager.VISIBILITY_NO_OVERRIDE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationVisibilityOverride{};
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b8bea9d102e1..56d0d1f2843d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -272,6 +273,7 @@ public class NotificationManager {
* {@link #AUTOMATIC_RULE_STATUS_UNKNOWN}.
* </p>
*/
+ // TODO (b/309101513): Add new status types to javadoc
public static final String EXTRA_AUTOMATIC_ZEN_RULE_STATUS =
"android.app.extra.AUTOMATIC_ZEN_RULE_STATUS";
@@ -286,7 +288,8 @@ public class NotificationManager {
/** @hide */
@IntDef(prefix = { "AUTOMATIC_RULE_STATUS" }, value = {
AUTOMATIC_RULE_STATUS_ENABLED, AUTOMATIC_RULE_STATUS_DISABLED,
- AUTOMATIC_RULE_STATUS_REMOVED, AUTOMATIC_RULE_STATUS_UNKNOWN
+ AUTOMATIC_RULE_STATUS_REMOVED, AUTOMATIC_RULE_STATUS_UNKNOWN,
+ AUTOMATIC_RULE_STATUS_ACTIVATED, AUTOMATIC_RULE_STATUS_DEACTIVATED
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutomaticZenRuleStatus {}
@@ -320,6 +323,27 @@ public class NotificationManager {
public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3;
/**
+ * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule has been
+ * activated by the user or cross device sync. Sent from
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. If the rule owner has a mode that includes
+ * a DND component, the rule owner should activate any extra behavior that's part of that mode
+ * in response to this broadcast.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int AUTOMATIC_RULE_STATUS_ACTIVATED = 4;
+
+ /**
+ * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule has been
+ * deactivated ("snoozed") by the user. The rule will not return to an activated state until
+ * the app calls {@link #setAutomaticZenRuleState(String, Condition)} with
+ * {@link Condition#STATE_FALSE} (either immediately or when the trigger criteria is no
+ * longer met) and then {@link Condition#STATE_TRUE} when the trigger criteria is freshly met,
+ * or when the user re-activates it.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int AUTOMATIC_RULE_STATUS_DEACTIVATED = 5;
+
+ /**
* Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes.
*
* <p>This broadcast is only sent to registered receivers and (starting from
@@ -1223,6 +1247,22 @@ public class NotificationManager {
}
/**
+ * Returns true if users can independently and fully manage {@link AutomaticZenRule} rules. This
+ * includes the ability to independently activate/deactivate rules and overwrite/freeze the
+ * behavior (policy) of the rule when activated.
+ * <p>
+ * If this method returns true, calls to
+ * {@link #updateAutomaticZenRule(String, AutomaticZenRule)} may fail and apps should defer
+ * rule management to system settings/uis.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean areAutomaticZenRulesUserManaged() {
+ // modes ui is dependent on modes api
+ return Flags.modesApi() && Flags.modesUi();
+ }
+
+
+ /**
* Returns AutomaticZenRules owned by the caller.
*
* <p>
@@ -1232,17 +1272,21 @@ public class NotificationManager {
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
INotificationManager service = getService();
try {
- List<ZenModeConfig.ZenRule> rules = service.getZenRules();
- Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
- for (ZenModeConfig.ZenRule rule : rules) {
- AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
- rule.configurationActivity, rule.conditionId, rule.zenPolicy,
- zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime);
- azr.setPackageName(rule.pkg);
- ruleMap.put(rule.id, azr);
+ if (Flags.modesApi()) {
+ return service.getAutomaticZenRules();
+ } else {
+ List<ZenModeConfig.ZenRule> rules = service.getZenRules();
+ Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
+ for (ZenModeConfig.ZenRule rule : rules) {
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity, rule.conditionId, rule.zenPolicy,
+ zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+ rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ ruleMap.put(rule.id, azr);
+ }
+ return ruleMap;
}
- return ruleMap;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1618,6 +1662,7 @@ public class NotificationManager {
*
* <p>
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public Policy getNotificationPolicy() {
INotificationManager service = getService();
try {
@@ -1636,6 +1681,7 @@ public class NotificationManager {
*
* @param policy The new desired policy.
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public void setNotificationPolicy(@NonNull Policy policy) {
checkRequired("policy", policy);
INotificationManager service = getService();
@@ -2584,6 +2630,7 @@ public class NotificationManager {
* Only available if policy access is granted to this package. See
* {@link #isNotificationPolicyAccessGranted}.
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
final INotificationManager service = getService();
try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fa52968e4554..79a5879b5cc0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -113,6 +113,7 @@ import android.hardware.iris.IrisManager;
import android.hardware.lights.LightsManager;
import android.hardware.lights.SystemLightsManager;
import android.hardware.location.ContextHubManager;
+import android.hardware.location.IContextHubService;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
@@ -1118,8 +1119,12 @@ public final class SystemServiceRegistry {
new CachedServiceFetcher<ContextHubManager>() {
@Override
public ContextHubManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new ContextHubManager(ctx.getOuterContext(),
- ctx.mMainThread.getHandler().getLooper());
+ IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE);
+ if (b == null) {
+ return null;
+ }
+ return new ContextHubManager(IContextHubService.Stub.asInterface(b),
+ ctx.mMainThread.getHandler().getLooper());
}});
registerService(Context.INCIDENT_SERVICE, IncidentManager.class,
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 019a1a8cb2a6..46216343dcff 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -42,6 +42,8 @@ import android.view.Surface;
import android.view.WindowManager;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -120,6 +122,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
WINDOWING_MODE_PINNED,
WINDOWING_MODE_FREEFORM,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface WindowingMode {}
/** The current activity type of the configuration. */
@@ -147,6 +150,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
ACTIVITY_TYPE_ASSISTANT,
ACTIVITY_TYPE_DREAM,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ActivityType {}
/** The current always on top status of the configuration. */
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index e4ee959336a5..14462b853c02 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -47,6 +47,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -175,6 +177,7 @@ public final class DeviceAdminInfo implements Parcelable {
public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
@IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED})
+ @Retention(RetentionPolicy.SOURCE)
private @interface HeadlessDeviceOwnerMode {}
/** @hide */
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 84b1ca5c6a61..b0bec783555b 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -164,11 +164,8 @@ public final class DevicePolicyIdentifiers {
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
- @TestApi
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c70c914ff21..fc3a906ced1d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -81,6 +81,9 @@ import android.app.Activity;
import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -3995,8 +3998,7 @@ public class DevicePolicyManager {
/**
* An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which
- * resource IDs (see {@link DevicePolicyResources.Drawables} and
- * {@link DevicePolicyResources.Strings}) have been updated.
+ * resource IDs (i.e. strings and drawables) have been updated.
*/
public static final String EXTRA_RESOURCE_IDS =
"android.app.extra.RESOURCE_IDS";
@@ -8367,9 +8369,7 @@ public class DevicePolicyManager {
* Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
* successfully set or not. This callback will contain:
* <ul>
- * <li> The policy identifier returned from
- * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)} with user restriction
- * {@link UserManager#DISALLOW_CAMERA}
+ * <li> The policy identifier: userRestriction_no_camera
* <li> The {@link TargetUser} that this policy relates to
* <li> The {@link PolicyUpdateResult}, which will be
* {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
@@ -9118,6 +9118,19 @@ public class DevicePolicyManager {
}
/**
+ * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
+ * {@link #isDeviceOwnerApp} method will use the user contained within the
+ * context.
+ * For apps targeting an SDK version <em>below</em> this, the user of the calling process will
+ * be used (Process.myUserHandle()).
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long IS_DEVICE_OWNER_USER_AWARE = 307233716L;
+
+ /**
* Used to determine if a particular package has been registered as a Device Owner app.
* A device owner app is a special device admin that cannot be deactivated by the user, once
* activated as a device admin. It also cannot be uninstalled. To check whether a particular
@@ -9130,8 +9143,13 @@ public class DevicePolicyManager {
* app, if any.
* @return whether or not the package is registered as the device owner app.
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isDeviceOwnerApp(String packageName) {
throwIfParentInstance("isDeviceOwnerApp");
+ if (android.permission.flags.Flags.roleControllerInSystemServer()
+ && CompatChanges.isChangeEnabled(IS_DEVICE_OWNER_USER_AWARE)) {
+ return isDeviceOwnerAppOnContextUser(packageName);
+ }
return isDeviceOwnerAppOnCallingUser(packageName);
}
@@ -9192,6 +9210,24 @@ public class DevicePolicyManager {
return packageName.equals(deviceOwner.getPackageName());
}
+ private boolean isDeviceOwnerAppOnContextUser(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ ComponentName deviceOwner = null;
+ if (mService != null) {
+ try {
+ deviceOwner = mService.getDeviceOwnerComponentOnUser(myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ if (deviceOwner == null) {
+ return false;
+ }
+ return packageName.equals(deviceOwner.getPackageName());
+ }
+
private ComponentName getDeviceOwnerComponentInner(boolean callingUserOnly) {
if (mService != null) {
try {
@@ -9608,6 +9644,7 @@ public class DevicePolicyManager {
* @param packageName The package name of the app to compare with the registered profile owner.
* @return Whether or not the package is registered as the profile owner.
*/
+ @UserHandleAware
public boolean isProfileOwnerApp(String packageName) {
throwIfParentInstance("isProfileOwnerApp");
if (mService != null) {
@@ -13363,7 +13400,7 @@ public class DevicePolicyManager {
* A device owner, by default, may continue granting these permissions. However, for increased
* user control, the admin may opt out of controlling grants for these permissions by including
* {@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters.
- * In that case the device owner's control will be limited do denying these permissions.
+ * In that case the device owner's control will be limited to denying these permissions.
* <p>
* NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
* the following permissions are restricted for managed profile owners:
@@ -17073,19 +17110,19 @@ public class DevicePolicyManager {
* Returns {@code true} if this device is marked as a financed device.
*
* <p>A financed device can be entered into lock task mode (see {@link #setLockTaskPackages})
- * by the holder of the role {@link android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK}.
+ * by the holder of the role {@code android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK}.
* If this occurs, Device Owners and Profile Owners that have set lock task packages or
* features, or that attempt to set lock task packages or features, will receive a callback
* indicating that it could not be set. See {@link PolicyUpdateReceiver#onPolicyChanged} and
* {@link PolicyUpdateReceiver#onPolicySetResult}.
*
* <p>To be informed of changes to this status you can subscribe to the broadcast
- * {@link ACTION_DEVICE_FINANCING_STATE_CHANGED}.
+ * {@link #ACTION_DEVICE_FINANCING_STATE_CHANGED}.
*
* @throws SecurityException if the caller is not a device owner, profile owner of an
* organization-owned managed profile, profile owner on the primary user or holder of one of the
- * following roles: {@link android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT},
- * android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION.
+ * following roles: {@code android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT},
+ * {@code android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION}.
*/
public boolean isDeviceFinanced() {
throwIfParentInstance("isDeviceFinanced");
@@ -17127,6 +17164,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @UnsupportedAppUsage
public boolean isOnboardingBugreportV2FlagEnabled() {
return onboardingBugreportV2Enabled();
}
diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java
index 2cc189f87ced..7a7123167771 100644
--- a/core/java/android/app/admin/DevicePolicyResourcesManager.java
+++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java
@@ -452,7 +452,7 @@ public class DevicePolicyResourcesManager {
/**
* Returns the appropriate updated string for the {@code stringId} (see
- * {@link DevicePolicyResources.Strings}) if one was set using
+ * {@code DevicePolicyResources.Strings}) if one was set using
* {@code setStrings}, otherwise returns the string from {@code defaultStringLoader}.
*
* <p>Also returns the string from {@code defaultStringLoader} if {@code stringId} is
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6fe40be041cc..575fa4cac0b8 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -179,6 +179,7 @@ interface IDevicePolicyManager {
boolean setDeviceOwner(in ComponentName who, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
+ ComponentName getDeviceOwnerComponentOnUser(int userId);
boolean hasDeviceOwner();
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index a6595feed1f7..b5c66ffa72a1 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -86,7 +86,9 @@ public final class AmbientContextEvent implements Parcelable {
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
EVENT_VENDOR_WEARABLE_START,
- }) public @interface EventCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventCode {}
/** The integer indicating an unknown level. */
public static final int LEVEL_UNKNOWN = 0;
@@ -114,7 +116,9 @@ public final class AmbientContextEvent implements Parcelable {
LEVEL_MEDIUM,
LEVEL_MEDIUM_HIGH,
LEVEL_HIGH
- }) public @interface LevelValue {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LevelValue {}
@EventCode private final int mEventType;
private static int defaultEventType() {
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
index bf383f1165c4..159481f82e39 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -32,6 +32,8 @@ import android.os.RemoteException;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -106,7 +108,9 @@ public final class AmbientContextManager {
STATUS_SERVICE_UNAVAILABLE,
STATUS_MICROPHONE_DISABLED,
STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusCode {}
/**
* Allows clients to retrieve the list of {@link AmbientContextEvent}s from the intent.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 0b8d1dfccc07..6b558d07c059 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -184,6 +184,14 @@ public abstract class BackupAgent extends ContextWrapper {
public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
/**
+ * Flag for {@link RestoreSet#backupTransportFlags} to indicate if restore should be skipped
+ * for apps that have already been launched.
+ *
+ * @hide
+ */
+ public static final int FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS = 1 << 2;
+
+ /**
* Flag for {@link BackupDataOutput#getTransportFlags()} and
* {@link FullBackupDataOutput#getTransportFlags()} only.
*
diff --git a/core/java/android/app/cloudsearch/SearchResponse.java b/core/java/android/app/cloudsearch/SearchResponse.java
index c86142e0d22d..dab1657d60d0 100644
--- a/core/java/android/app/cloudsearch/SearchResponse.java
+++ b/core/java/android/app/cloudsearch/SearchResponse.java
@@ -21,6 +21,8 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -37,6 +39,7 @@ public final class SearchResponse implements Parcelable {
SEARCH_STATUS_OK,
SEARCH_STATUS_TIME_OUT,
SEARCH_STATUS_NO_INTERNET})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SearchStatusCode {
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index d9b521f1a9a8..bf5bad3dc0cf 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -8,6 +8,13 @@ flag {
}
flag {
+ name: "modes_ui"
+ namespace: "systemui"
+ description: "This flag controls new and updated DND UIs; dependent on flag modes_api"
+ bug: "270703654"
+}
+
+flag {
name: "api_tvextender"
namespace: "systemui"
description: "Guards new android.app.Notification.TvExtender api"
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index dd332c850d5d..bc8fac5fa0ce 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -71,17 +71,13 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
@NonNull
public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
@NonNull Configuration config) {
- if (config == null) {
- throw new IllegalArgumentException("Config must not be null.");
- }
-
ActivityConfigurationChangeItem instance =
ObjectPool.obtain(ActivityConfigurationChangeItem.class);
if (instance == null) {
instance = new ActivityConfigurationChangeItem();
}
instance.setActivityToken(activityToken);
- instance.mConfiguration = config;
+ instance.mConfiguration = new Configuration(config);
return instance;
}
@@ -89,7 +85,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
@Override
public void recycle() {
super.recycle();
- mConfiguration = Configuration.EMPTY;
+ mConfiguration = null;
ObjectPool.recycle(this);
}
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 06bff5df490a..48db18f4a1d7 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -59,7 +59,7 @@ public abstract class ActivityLifecycleItem extends ActivityTransactionItem {
}
@Override
- boolean isActivityLifecycleItem() {
+ public boolean isActivityLifecycleItem() {
return true;
}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index a5dd115d78b3..3ce094ef7467 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -51,7 +52,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
/**
* A record that was properly configured for relaunch. Execution will be cancelled if not
- * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+ * initialized after {@link #preExecute(ClientTransactionHandler)}.
*/
private ActivityClientRecord mActivityClientRecord;
@@ -99,10 +100,11 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
instance = new ActivityRelaunchItem();
}
instance.setActivityToken(activityToken);
- instance.mPendingResults = pendingResults;
- instance.mPendingNewIntents = pendingNewIntents;
+ instance.mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null;
+ instance.mPendingNewIntents =
+ pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null;
instance.mConfigChanges = configChanges;
- instance.mConfig = config;
+ instance.mConfig = new MergedConfiguration(config);
instance.mPreserveWindow = preserveWindow;
return instance;
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 24fced4981d6..51a09fb59236 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -35,6 +35,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Trace;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -82,7 +83,7 @@ public class ActivityResultItem extends ActivityTransactionItem {
instance = new ActivityResultItem();
}
instance.setActivityToken(activityToken);
- instance.mResultInfoList = resultInfoList;
+ instance.mResultInfoList = new ArrayList<>(resultInfoList);
return instance;
}
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 2a65b3528145..b4ff476f4702 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -20,6 +20,8 @@ import static android.app.servertransaction.TransactionExecutorHelper.getActivit
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -93,7 +95,7 @@ public abstract class ActivityTransactionItem extends ClientTransactionItem {
}
void setActivityToken(@NonNull IBinder activityToken) {
- mActivityToken = activityToken;
+ mActivityToken = requireNonNull(activityToken);
}
// To be overridden
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 9c0cd39e8102..7c34cdefe95f 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -54,12 +54,14 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
/** A list of individual callbacks to a client. */
@UnsupportedAppUsage
+ @Nullable
private List<ClientTransactionItem> mActivityCallbacks;
/**
* Final lifecycle state in which the client activity should be after the transaction is
* executed.
*/
+ @Nullable
private ActivityLifecycleItem mLifecycleStateRequest;
/** Target client. */
@@ -123,6 +125,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
@VisibleForTesting(visibility = PACKAGE)
@UnsupportedAppUsage
@Deprecated
+ @Nullable
public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
}
@@ -207,7 +210,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
for (int i = 0; i < size; i++) {
mActivityCallbacks.get(i).recycle();
}
- mActivityCallbacks.clear();
+ mActivityCallbacks = null;
}
if (mLifecycleStateRequest != null) {
mLifecycleStateRequest.recycle();
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index f94e22de06e5..a8d61db1ce3a 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -75,7 +75,7 @@ public abstract class ClientTransactionItem implements BaseClientRequest, Parcel
/**
* Whether this is a {@link ActivityLifecycleItem}.
*/
- boolean isActivityLifecycleItem() {
+ public boolean isActivityLifecycleItem() {
return false;
}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 96961aced987..0e327a7627d1 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -64,7 +64,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem {
if (instance == null) {
instance = new ConfigurationChangeItem();
}
- instance.mConfiguration = config;
+ instance.mConfiguration = new Configuration(config);
instance.mDeviceId = deviceId;
return instance;
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index a64c744c70ba..d2ef65aec698 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -45,6 +45,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -135,10 +136,16 @@ public class LaunchActivityItem extends ClientTransactionItem {
if (instance == null) {
instance = new LaunchActivityItem();
}
- setValues(instance, activityToken, intent, ident, info, curConfig, overrideConfig, deviceId,
- referrer, voiceInteractor, procState, state, persistentState, pendingResults,
- pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
- activityClientController, shareableActivityToken,
+ setValues(instance, activityToken, new Intent(intent), ident, new ActivityInfo(info),
+ new Configuration(curConfig), new Configuration(overrideConfig), deviceId,
+ referrer, voiceInteractor, procState,
+ state != null ? new Bundle(state) : null,
+ persistentState != null ? new PersistableBundle(persistentState) : null,
+ pendingResults != null ? new ArrayList<>(pendingResults) : null,
+ pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
+ activityOptions, isForward,
+ profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
+ assistToken, activityClientController, shareableActivityToken,
launchedFromBubble, taskFragmentToken);
return instance;
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index e56d3f862b1b..961da19daeed 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -69,7 +69,7 @@ public class MoveToDisplayItem extends ActivityTransactionItem {
}
instance.setActivityToken(activityToken);
instance.mTargetDisplayId = targetDisplayId;
- instance.mConfiguration = configuration;
+ instance.mConfiguration = new Configuration(configuration);
return instance;
}
@@ -78,7 +78,7 @@ public class MoveToDisplayItem extends ActivityTransactionItem {
public void recycle() {
super.recycle();
mTargetDisplayId = 0;
- mConfiguration = Configuration.EMPTY;
+ mConfiguration = null;
ObjectPool.recycle(this);
}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 8e995aa05a48..acf2ea429e82 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -32,6 +32,7 @@ import android.os.Trace;
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -71,7 +72,7 @@ public class NewIntentItem extends ActivityTransactionItem {
instance = new NewIntentItem();
}
instance.setActivityToken(activityToken);
- instance.mIntents = intents;
+ instance.mIntents = new ArrayList<>(intents);
instance.mResume = resume;
return instance;
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index 375d1bf57174..cbad92ff3f38 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -65,7 +65,7 @@ public class WindowContextInfoChangeItem extends ClientTransactionItem {
instance = new WindowContextInfoChangeItem();
}
instance.mClientToken = requireNonNull(clientToken);
- instance.mInfo = new WindowContextInfo(config, displayId);
+ instance.mInfo = new WindowContextInfo(new Configuration(config), displayId);
return instance;
}
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 98281338872b..7d3eb8783c04 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -77,10 +77,10 @@ public class WindowStateResizeItem extends ClientTransactionItem {
instance = new WindowStateResizeItem();
}
instance.mWindow = requireNonNull(window);
- instance.mFrames = requireNonNull(frames);
+ instance.mFrames = new ClientWindowFrames(frames);
instance.mReportDraw = reportDraw;
- instance.mConfiguration = requireNonNull(configuration);
- instance.mInsetsState = requireNonNull(insetsState);
+ instance.mConfiguration = new MergedConfiguration(configuration);
+ instance.mInsetsState = new InsetsState(insetsState);
instance.mForceLayout = forceLayout;
instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
instance.mDisplayId = displayId;
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index d0b4fe473148..f1ca0864a6dd 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -35,6 +35,8 @@ import android.os.SharedMemory;
import android.service.wearable.WearableSensingService;
import android.system.OsConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -105,7 +107,9 @@ public class WearableSensingManager {
STATUS_SERVICE_UNAVAILABLE,
STATUS_WEARABLE_UNAVAILABLE,
STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusCode {}
private final Context mContext;
private final IWearableSensingManager mService;
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
new file mode 100644
index 000000000000..6a735a418b58
--- /dev/null
+++ b/core/java/android/appwidget/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.appwidget.flags"
+
+flag {
+ name: "generated_previews"
+ namespace: "app_widgets"
+ description: "Enable support for generated previews in AppWidgetManager"
+ bug: "306546610"
+}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 9ea3dfc61ef1..e0ce917fa3b3 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -33,6 +33,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -795,9 +796,19 @@ public final class CompanionDeviceManager {
@UserHandleAware
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public @NonNull List<AssociationInfo> getAllAssociations() {
+ return getAllAssociations(mContext.getUserId());
+ }
+
+ /**
+ * Per-user version of {@link #getAllAssociations()}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public @NonNull List<AssociationInfo> getAllAssociations(@UserIdInt int userId) {
if (!checkFeaturePresent()) return Collections.emptyList();
try {
- return mService.getAllAssociationsForUser(mContext.getUserId());
+ return mService.getAllAssociationsForUser(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -830,12 +841,25 @@ public final class CompanionDeviceManager {
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public void addOnAssociationsChangedListener(
@NonNull Executor executor, @NonNull OnAssociationsChangedListener listener) {
+ addOnAssociationsChangedListener(executor, listener, mContext.getUserId());
+ }
+
+ /**
+ * Per-user version of
+ * {@link #addOnAssociationsChangedListener(Executor, OnAssociationsChangedListener)}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public void addOnAssociationsChangedListener(
+ @NonNull Executor executor, @NonNull OnAssociationsChangedListener listener,
+ @UserIdInt int userId) {
if (!checkFeaturePresent()) return;
synchronized (mListeners) {
final OnAssociationsChangedListenerProxy proxy = new OnAssociationsChangedListenerProxy(
executor, listener);
try {
- mService.addOnAssociationsChangedListener(proxy, mContext.getUserId());
+ mService.addOnAssociationsChangedListener(proxy, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 2f97080901f9..102cbf3a7e31 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -23,8 +23,7 @@ import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.graphics.Point;
@@ -236,8 +235,15 @@ interface IVirtualDevice {
void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
/**
- * Creates a new VirtualCamera and registers it with the VirtualCameraProvider.
+ * Creates a new virtual camera and registers it with the virtual camera service.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void registerVirtualCamera(in IVirtualCamera camera);
+ void registerVirtualCamera(in VirtualCameraConfig camera);
+
+ /**
+ * Destroys the virtual camera with given config and unregisters it from the virtual camera
+ * service.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void unregisterVirtualCamera(in VirtualCameraConfig camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 93a3e7822888..d0c8be6ca896 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -32,9 +32,8 @@ import android.os.RemoteException;
* Details of a particular virtual device.
*
* <p>Read-only device representation exposing the properties of an existing virtual device.
- *
- * @see VirtualDeviceManager#registerVirtualDeviceListener
*/
+// TODO(b/310912420): Link to VirtualDeviceManager#registerVirtualDeviceListener from the docs
public final class VirtualDevice implements Parcelable {
private final @NonNull IVirtualDevice mVirtualDevice;
@@ -92,8 +91,8 @@ public final class VirtualDevice implements Parcelable {
* per device.
*
* @see Context#createDeviceContext
- * @see #getPersistentDeviceId
*/
+ // TODO(b/310912420): Link to #getPersistentDeviceId from the docs
public int getDeviceId() {
return mId;
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index f6a7d2a465fb..da8277c24f6c 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -271,7 +271,7 @@ public class VirtualDeviceInternal {
final IBinder token = new Binder(
"android.hardware.input.VirtualDpad:" + config.getInputDeviceName());
mVirtualDevice.createVirtualDpad(config, token);
- return new VirtualDpad(mVirtualDevice, token);
+ return new VirtualDpad(config, mVirtualDevice, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -283,7 +283,7 @@ public class VirtualDeviceInternal {
final IBinder token = new Binder(
"android.hardware.input.VirtualKeyboard:" + config.getInputDeviceName());
mVirtualDevice.createVirtualKeyboard(config, token);
- return new VirtualKeyboard(mVirtualDevice, token);
+ return new VirtualKeyboard(config, mVirtualDevice, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -295,7 +295,7 @@ public class VirtualDeviceInternal {
final IBinder token = new Binder(
"android.hardware.input.VirtualMouse:" + config.getInputDeviceName());
mVirtualDevice.createVirtualMouse(config, token);
- return new VirtualMouse(mVirtualDevice, token);
+ return new VirtualMouse(config, mVirtualDevice, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -308,7 +308,7 @@ public class VirtualDeviceInternal {
final IBinder token = new Binder(
"android.hardware.input.VirtualTouchscreen:" + config.getInputDeviceName());
mVirtualDevice.createVirtualTouchscreen(config, token);
- return new VirtualTouchscreen(mVirtualDevice, token);
+ return new VirtualTouchscreen(config, mVirtualDevice, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -322,7 +322,7 @@ public class VirtualDeviceInternal {
"android.hardware.input.VirtualNavigationTouchpad:"
+ config.getInputDeviceName());
mVirtualDevice.createVirtualNavigationTouchpad(config, token);
- return new VirtualNavigationTouchpad(mVirtualDevice, token);
+ return new VirtualNavigationTouchpad(config, mVirtualDevice, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index b3ea93bb8a85..41c90b96dc84 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -149,6 +149,19 @@ public final class VirtualDeviceManager {
@SystemApi
public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2;
+ /**
+ * Persistent device identifier corresponding to the default device.
+ *
+ * @see Context#DEVICE_ID_DEFAULT
+ * @see VirtualDevice#getPersistentDeviceId()
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
+ public static final String PERSISTENT_DEVICE_ID_DEFAULT =
+ "default:" + Context.DEVICE_ID_DEFAULT;
+
private final IVirtualDeviceManager mService;
private final Context mContext;
@@ -199,9 +212,10 @@ public final class VirtualDeviceManager {
* existing virtual devices.</p>
*
* <p>Note that if a virtual device is closed and becomes invalid, the returned objects will
- * not be updated and may contain stale values. Use a {@link VirtualDeviceListener} for real
- * time updates of the availability of virtual devices.</p>
+ * not be updated and may contain stale values.</p>
*/
+ // TODO(b/310912420): Add "Use a VirtualDeviceListener for real time updates of the
+ // availability of virtual devices." in the note paragraph above with a link annotation.
@NonNull
public List<android.companion.virtual.VirtualDevice> getVirtualDevices() {
if (mService == null) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 97a7aa4a3bea..0d73e44f5197 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -37,6 +37,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
import android.content.ComponentName;
import android.content.Context;
+import android.hardware.display.VirtualDisplayConfig;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SharedMemory;
@@ -326,8 +327,8 @@ public final class VirtualDeviceParams implements Parcelable {
* support home activities.
*
* @see Builder#setHomeComponent
+ * @see VirtualDisplayConfig#isHomeSupported()
*/
- // TODO(b/297168328): Link to the relevant API for creating displays with home support.
@FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@Nullable
public ComponentName getHomeComponent() {
@@ -737,8 +738,9 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @param homeComponent The component name to be used as home. If unset, then the system-
* default secondary home activity will be used.
+ *
+ * @see VirtualDisplayConfig#isHomeSupported()
*/
- // TODO(b/297168328): Link to the relevant API for creating displays with home support.
@FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@NonNull
public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
diff --git a/core/java/android/companion/virtual/camera/IVirtualCamera.aidl b/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
deleted file mode 100644
index 58b850dac41c..000000000000
--- a/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
+++ /dev/null
@@ -1,17 +0,0 @@
-package android.companion.virtual.camera;
-
-import android.companion.virtual.camera.IVirtualCameraSession;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-
-/**
- * Counterpart of ICameraDevice for virtual camera.
- *
- * @hide
- */
-interface IVirtualCamera {
-
- IVirtualCameraSession open();
-
- VirtualCameraHalConfig getHalConfig();
-
-} \ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
new file mode 100644
index 000000000000..fac44b50024f
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.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.companion.virtual.camera;
+
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtual.camera.VirtualCameraMetadata;
+import android.view.Surface;
+
+/**
+ * Interface for the virtual camera service and system server to talk back to the virtual camera owner.
+ *
+ * @hide
+ */
+interface IVirtualCameraCallback {
+
+ /**
+ * Called when one of the requested stream has been configured by the virtual camera service and
+ * is ready to receive data onto its {@link Surface}
+ *
+ * @param streamId The id of the configured stream
+ * @param surface The surface to write data into for this stream
+ * @param streamConfig The image data configuration for this stream
+ */
+ oneway void onStreamConfigured(
+ int streamId,
+ in Surface surface,
+ in VirtualCameraStreamConfig streamConfig);
+
+ /**
+ * The client application is requesting a camera frame for the given streamId with the provided
+ * metadata.
+ *
+ * <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
+ * this stream that was provided during the {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} call.
+ *
+ * @param streamId The streamId for which the frame is requested. This corresponds to the
+ * streamId that was given in {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)}
+ * @param frameId The frameId that is being requested. Each request will have a different
+ * frameId, that will be increasing for each call with a particular streamId.
+ * @param metadata The metadata requested for the frame. The virtual camera should do its best
+ * to honor the requested metadata.
+ */
+ oneway void onProcessCaptureRequest(
+ int streamId, long frameId, in VirtualCameraMetadata metadata);
+
+ /**
+ * The stream previously configured when {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
+ * freed. The Surface was disposed on the client side and should not be used anymore by the virtual camera owner
+ *
+ * @param streamId The id of the stream that was closed.
+ */
+ oneway void onStreamClosed(int streamId);
+
+} \ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index 791bf0a1ff1f..beee86fcfac2 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -16,20 +16,44 @@
package android.companion.virtual.camera;
+import android.annotation.FlaggedApi;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.flags.Flags;
+import android.hardware.camera2.CameraDevice;
import android.os.RemoteException;
import androidx.annotation.NonNull;
+import java.io.Closeable;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
- * Virtual camera that is used to send image data into system.
+ * A VirtualCamera is the representation of a remote or computer generated camera that will be
+ * exposed to applications using the Android Camera APIs.
*
+ * <p>A VirtualCamera is created using {@link
+ * VirtualDeviceManager.VirtualDevice#createVirtualCamera(VirtualCameraConfig)}.
+ *
+ * <p>Once a virtual camera is created, it will receive callbacks from the system when an
+ * application attempts to use it via the {@link VirtualCameraCallback} class set using {@link
+ * VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)}
+ *
+ * @see VirtualDeviceManager.VirtualDevice#createVirtualDevice(int, VirtualDeviceParams)
+ * @see VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)
+ * @see android.hardware.camera2.CameraManager#openCamera(String, CameraDevice.StateCallback,
+ * android.os.Handler)
* @hide
*/
-public final class VirtualCamera extends IVirtualCamera.Stub {
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCamera implements Closeable {
+ private final IVirtualDevice mVirtualDevice;
private final VirtualCameraConfig mConfig;
/**
@@ -37,40 +61,35 @@ public final class VirtualCamera extends IVirtualCamera.Stub {
*
* @param virtualDevice The Binder object representing this camera in the server.
* @param config Configuration for the new virtual camera
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public VirtualCamera(
@NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
+ mVirtualDevice = virtualDevice;
mConfig = Objects.requireNonNull(config);
Objects.requireNonNull(virtualDevice);
+ // TODO(b/310857519): Avoid registration inside constructor.
try {
- virtualDevice.registerVirtualCamera(this);
+ mVirtualDevice.registerVirtualCamera(config);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
- /** Get the camera session associated with this device */
- @Override
- public IVirtualCameraSession open() {
- // TODO: b/302255544 - Make this async.
- VirtualCameraSession session = mConfig.getCallback().onOpenSession();
- return new VirtualCameraSessionInternal(session);
- }
-
/** Returns the configuration of this virtual camera instance. */
@NonNull
public VirtualCameraConfig getConfig() {
return mConfig;
}
- /**
- * Returns the configuration to be used by the virtual camera HAL.
- *
- * @hide
- */
@Override
- @NonNull
- public VirtualCameraHalConfig getHalConfig() {
- return mConfig.getHalConfig();
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterVirtualCamera(mConfig);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
index a7c3d4fac7dc..a18ae03555e9 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
@@ -16,7 +16,12 @@
package android.companion.virtual.camera;
-import android.hardware.camera2.params.SessionConfiguration;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.view.Surface;
import java.util.concurrent.Executor;
@@ -24,15 +29,53 @@ import java.util.concurrent.Executor;
* Interface to be provided when creating a new {@link VirtualCamera} in order to receive callbacks
* from the framework and the camera system.
*
- * @see VirtualCameraConfig.Builder#setCallback(Executor, VirtualCameraCallback)
+ * @see VirtualCameraConfig.Builder#setVirtualCameraCallback(Executor, VirtualCameraCallback)
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public interface VirtualCameraCallback {
/**
- * Called when a client opens a new camera session for the associated {@link VirtualCamera}
+ * Called when one of the requested stream has been configured by the virtual camera service and
+ * is ready to receive data onto its {@link Surface}
*
- * @see android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)
+ * @param streamId The id of the configured stream
+ * @param surface The surface to write data into for this stream
+ * @param streamConfig The image data configuration for this stream
*/
- VirtualCameraSession onOpenSession();
+ void onStreamConfigured(
+ int streamId,
+ @NonNull Surface surface,
+ @NonNull VirtualCameraStreamConfig streamConfig);
+
+ /**
+ * The client application is requesting a camera frame for the given streamId with the provided
+ * metadata.
+ *
+ * <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
+ * this stream that was provided during the {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} call.
+ *
+ * @param streamId The streamId for which the frame is requested. This corresponds to the
+ * streamId that was given in {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)}
+ * @param frameId The frameId that is being requested. Each request will have a different
+ * frameId, that will be increasing for each call with a particular streamId.
+ * @param metadata The metadata requested for the frame. The virtual camera should do its best
+ * to honor the requested metadata but the consumer won't be informed about the metadata set
+ * for a particular frame. If null, the requested frame can be anything the producer sends.
+ */
+ void onProcessCaptureRequest(
+ int streamId, long frameId, @Nullable VirtualCameraMetadata metadata);
+
+ /**
+ * The stream previously configured when {@link #onStreamConfigured(int, Surface,
+ * VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
+ * freed. The Surface corresponding to that streamId was disposed on the client side and should
+ * not be used anymore by the virtual camera owner
+ *
+ * @param streamId The id of the stream that was closed.
+ */
+ void onStreamClosed(int streamId);
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSession.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl
index c25d97711e75..88c27a52bb9d 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraSession.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.aidl
@@ -13,18 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.companion.virtual.camera;
-/***
- * Counterpart of {@link android.hardware.camera2.CameraCaptureSession} for producing
- * images from a {@link VirtualCamera}.
- * @hide
- */
-// TODO: b/289881985 - This is just a POC implementation for now, this will be extended
-// to a full featured Camera Session
-public interface VirtualCameraSession {
-
- /** Close the session and release its resources. */
- default void close() {}
-}
+/** @hide */
+parcelable VirtualCameraConfig; \ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index fb464d53b9b5..f1eb240301e0 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -18,11 +18,19 @@ package android.companion.virtual.camera;
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.content.res.Resources;
import android.graphics.ImageFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArraySet;
+import android.view.Surface;
-import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -33,33 +41,115 @@ import java.util.concurrent.Executor;
*
* @hide
*/
-public final class VirtualCameraConfig {
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraConfig implements Parcelable {
- private final String mDisplayName;
+ private final @StringRes int mNameStringRes;
private final Set<VirtualCameraStreamConfig> mStreamConfigurations;
- private final VirtualCameraCallback mCallback;
- private final Executor mCallbackExecutor;
+ private final IVirtualCameraCallback mCallback;
+
+ private VirtualCameraConfig(
+ int displayNameStringRes,
+ @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
+ @NonNull Executor executor,
+ @NonNull VirtualCameraCallback callback) {
+ mNameStringRes = displayNameStringRes;
+ mStreamConfigurations =
+ Set.copyOf(requireNonNull(streamConfigurations, "Missing stream configurations"));
+ if (mStreamConfigurations.isEmpty()) {
+ throw new IllegalArgumentException(
+ "At least one stream configuration is needed to create a virtual camera.");
+ }
+ mCallback =
+ new VirtualCameraCallbackInternal(
+ requireNonNull(callback, "Missing callback"),
+ requireNonNull(executor, "Missing callback executor"));
+ }
+
+ private VirtualCameraConfig(@NonNull Parcel in) {
+ mNameStringRes = in.readInt();
+ mCallback = IVirtualCameraCallback.Stub.asInterface(in.readStrongBinder());
+ mStreamConfigurations =
+ Set.of(
+ in.readParcelableArray(
+ VirtualCameraStreamConfig.class.getClassLoader(),
+ VirtualCameraStreamConfig.class));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mNameStringRes);
+ dest.writeStrongInterface(mCallback);
+ dest.writeParcelableArray(
+ mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]), flags);
+ }
+
+ /**
+ * @return The display name of this VirtualCamera
+ */
+ @StringRes
+ public int getDisplayNameStringRes() {
+ return mNameStringRes;
+ }
+
+ /**
+ * Returns an unmodifiable set of the stream configurations added to this {@link
+ * VirtualCameraConfig}.
+ *
+ * @see VirtualCameraConfig.Builder#addStreamConfig(int, int, int)
+ */
+ @NonNull
+ public Set<VirtualCameraStreamConfig> getStreamConfigs() {
+ return mStreamConfigurations;
+ }
+
+ /**
+ * Returns the callback used to communicate from the server to the client.
+ *
+ * @hide
+ */
+ @NonNull
+ public IVirtualCameraCallback getCallback() {
+ return mCallback;
+ }
/**
* Builder for {@link VirtualCameraConfig}.
*
* <p>To build an instance of {@link VirtualCameraConfig} the following conditions must be met:
- * <li>At least one stream must be added wit {@link #addStreamConfiguration(int, int, int)}.
- * <li>A name must be set with {@link #setDisplayName(String)}
- * <li>A callback must be set wit {@link #setCallback(Executor, VirtualCameraCallback)}
+ * <li>At least one stream must be added with {@link #addStreamConfig(int, int, int)}.
+ * <li>A callback must be set with {@link #setVirtualCameraCallback(Executor,
+ * VirtualCameraCallback)}
+ * <li>A user readable name can be set with {@link #setDisplayNameStringRes(int)}
*/
+ @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public static final class Builder {
- private String mDisplayName;
- private final ArraySet<VirtualCameraStreamConfig> mStreamConfiguration = new ArraySet<>();
+ private @StringRes int mDisplayNameStringRes = Resources.ID_NULL;
+ private final ArraySet<VirtualCameraStreamConfig> mStreamConfigurations = new ArraySet<>();
private Executor mCallbackExecutor;
private VirtualCameraCallback mCallback;
- /** Set the visible name of this camera for the user. */
- // TODO: b/290172356 - Take a resource id instead of displayName
+ /**
+ * Set the visible name of this camera for the user.
+ *
+ * <p>Sets the resource to a string representing a user readable name for this virtual
+ * camera.
+ *
+ * @throws IllegalArgumentException if an invalid resource id is passed.
+ */
@NonNull
- public Builder setDisplayName(@NonNull String displayName) {
- mDisplayName = requireNonNull(displayName);
+ public Builder setDisplayNameStringRes(@StringRes int displayNameStringRes) {
+ if (displayNameStringRes <= 0) {
+ throw new IllegalArgumentException("Invalid resource passed for display name");
+ }
+ mDisplayNameStringRes = displayNameStringRes;
return this;
}
@@ -68,18 +158,21 @@ public final class VirtualCameraConfig {
*
* <p>At least one {@link VirtualCameraStreamConfig} must be added.
*
- * @param width The width of the stream
- * @param height The height of the stream
- * @param format The {@link ImageFormat} of the stream
+ * @param width The width of the stream.
+ * @param height The height of the stream.
+ * @param format The {@link ImageFormat} of the stream.
+ *
+ * @throws IllegalArgumentException if invalid format or dimensions are passed.
*/
@NonNull
- public Builder addStreamConfiguration(
- int width, int height, @ImageFormat.Format int format) {
- VirtualCameraStreamConfig streamConfig = new VirtualCameraStreamConfig();
- streamConfig.width = width;
- streamConfig.height = height;
- streamConfig.format = format;
- mStreamConfiguration.add(streamConfig);
+ public Builder addStreamConfig(int width, int height, @ImageFormat.Format int format) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Invalid dimensions passed for stream config");
+ }
+ if (!ImageFormat.isPublicFormat(format)) {
+ throw new IllegalArgumentException("Invalid format passed for stream config");
+ }
+ mStreamConfigurations.add(new VirtualCameraStreamConfig(width, height, format));
return this;
}
@@ -93,7 +186,9 @@ public final class VirtualCameraConfig {
* @param callback The instance of the callback to be added. Subsequent call to this method
* will replace the callback set.
*/
- public Builder setCallback(
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder") // The configuration is immutable
+ public Builder setVirtualCameraCallback(
@NonNull Executor executor, @NonNull VirtualCameraCallback callback) {
mCallbackExecutor = requireNonNull(executor);
mCallback = requireNonNull(callback);
@@ -108,67 +203,49 @@ public final class VirtualCameraConfig {
@NonNull
public VirtualCameraConfig build() {
return new VirtualCameraConfig(
- mDisplayName, mStreamConfiguration, mCallbackExecutor, mCallback);
+ mDisplayNameStringRes, mStreamConfigurations, mCallbackExecutor, mCallback);
}
}
- private VirtualCameraConfig(
- @NonNull String displayName,
- @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
- @NonNull Executor executor,
- @NonNull VirtualCameraCallback callback) {
- mDisplayName = requireNonNull(displayName, "Missing display name");
- mStreamConfigurations =
- Collections.unmodifiableSet(
- requireNonNull(streamConfigurations, "Missing stream configuration"));
- if (mStreamConfigurations.isEmpty()) {
- throw new IllegalArgumentException(
- "At least one StreamConfiguration is needed to create a virtual camera.");
- }
- mCallback = requireNonNull(callback, "Missing callback");
- mCallbackExecutor = requireNonNull(executor, "Missing callback executor");
- }
+ private static class VirtualCameraCallbackInternal extends IVirtualCameraCallback.Stub {
- /**
- * @return The display name of this VirtualCamera
- */
- @NonNull
- public String getDisplayName() {
- return mDisplayName;
- }
+ private final VirtualCameraCallback mCallback;
+ private final Executor mExecutor;
- /**
- * Returns an unmodifiable set of the stream configurations added to this {@link
- * VirtualCameraConfig}.
- *
- * @see VirtualCameraConfig.Builder#addStreamConfiguration(int, int, int)
- */
- @NonNull
- public Set<VirtualCameraStreamConfig> getStreamConfigs() {
- return mStreamConfigurations;
- }
+ private VirtualCameraCallbackInternal(VirtualCameraCallback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = executor;
+ }
- /** Returns the callback used to communicate from the server to the client. */
- @NonNull
- public VirtualCameraCallback getCallback() {
- return mCallback;
- }
+ @Override
+ public void onStreamConfigured(
+ int streamId, Surface surface, VirtualCameraStreamConfig streamConfig) {
+ mExecutor.execute(() -> mCallback.onStreamConfigured(streamId, surface, streamConfig));
+ }
- /** Returns the executor onto which the callback should be run. */
- @NonNull
- public Executor getCallbackExecutor() {
- return mCallbackExecutor;
+ @Override
+ public void onProcessCaptureRequest(
+ int streamId, long frameId, VirtualCameraMetadata metadata) {
+ mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId, metadata));
+ }
+
+ @Override
+ public void onStreamClosed(int streamId) {
+ mExecutor.execute(() -> mCallback.onStreamClosed(streamId));
+ }
}
- /**
- * Returns a new instance of {@link VirtualCameraHalConfig} initialized with data from this
- * {@link VirtualCameraConfig}
- */
@NonNull
- public VirtualCameraHalConfig getHalConfig() {
- VirtualCameraHalConfig halConfig = new VirtualCameraHalConfig();
- halConfig.displayName = mDisplayName;
- halConfig.streamConfigs = mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]);
- return halConfig;
- }
+ public static final Parcelable.Creator<VirtualCameraConfig> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public VirtualCameraConfig createFromParcel(Parcel in) {
+ return new VirtualCameraConfig(in);
+ }
+
+ @Override
+ public VirtualCameraConfig[] newArray(int size) {
+ return new VirtualCameraConfig[size];
+ }
+ };
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
deleted file mode 100644
index 7070a38b6414..000000000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
+++ /dev/null
@@ -1,12 +0,0 @@
-package android.companion.virtual.camera;
-
-import android.companion.virtual.camera.VirtualCameraStreamConfig;
-
-/**
- * Configuration for VirtualCamera to be passed to the server and HAL service.
- * @hide
- */
-parcelable VirtualCameraHalConfig {
- String displayName;
- VirtualCameraStreamConfig[] streamConfigs;
-}
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
index 252980773264..6c1f0fcd622a 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
+++ b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
@@ -13,16 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.companion.virtual.camera;
/**
- * Counterpart of ICameraDeviceSession for virtual camera.
- *
+ * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
+ * VirtualCamera.
* @hide
*/
-interface IVirtualCameraSession {
-
- void configureStream(int width, int height, int format);
-
- void close();
-}
+parcelable VirtualCameraMetadata;
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
new file mode 100644
index 000000000000..1ba36d08cbeb
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
@@ -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 android.companion.virtual.camera;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data structure used to store camera metadata compatible with VirtualCamera.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraMetadata implements Parcelable {
+
+ /** @hide */
+ public VirtualCameraMetadata(@NonNull Parcel in) {}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ @NonNull
+ public static final Creator<VirtualCameraMetadata> CREATOR =
+ new Creator<>() {
+ @Override
+ @NonNull
+ public VirtualCameraMetadata createFromParcel(Parcel in) {
+ return new VirtualCameraMetadata(in);
+ }
+
+ @Override
+ @NonNull
+ public VirtualCameraMetadata[] newArray(int size) {
+ return new VirtualCameraMetadata[size];
+ }
+ };
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java b/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
deleted file mode 100644
index da168de41cee..000000000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
+++ /dev/null
@@ -1,44 +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.companion.virtual.camera;
-
-import android.graphics.ImageFormat;
-
-import androidx.annotation.NonNull;
-
-import java.util.Objects;
-
-/**
- * Wraps the client side {@link VirtualCameraSession} into an {@link IVirtualCameraSession}.
- *
- * @hide
- */
-final class VirtualCameraSessionInternal extends IVirtualCameraSession.Stub {
-
- @SuppressWarnings("FieldCanBeLocal")
- // TODO: b/289881985: Will be used once connected with the CameraService
- private final VirtualCameraSession mVirtualCameraSession;
-
- VirtualCameraSessionInternal(@NonNull VirtualCameraSession virtualCameraSession) {
- mVirtualCameraSession = Objects.requireNonNull(virtualCameraSession);
- }
-
- @Override
- public void configureStream(int width, int height, @ImageFormat.Format int format) {}
-
- public void close() {}
-}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
index 304d4553bd2a..ce92b6d48f0d 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
@@ -16,11 +16,7 @@
package android.companion.virtual.camera;
/**
- * A stream configuration supported by a virtual camera
+ * The configuration of a single virtual camera stream.
* @hide
*/
-parcelable VirtualCameraStreamConfig {
- int width;
- int height;
- int format;
-}
+parcelable VirtualCameraStreamConfig; \ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
new file mode 100644
index 000000000000..e198821e860e
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
@@ -0,0 +1,106 @@
+/*
+ * 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.companion.virtual.camera;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtual.flags.Flags;
+import android.graphics.ImageFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The configuration of a single virtual camera stream.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+public final class VirtualCameraStreamConfig implements Parcelable {
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+
+ /**
+ * Construct a new instance of {@link VirtualCameraStreamConfig} initialized with the provided
+ * width, height and {@link ImageFormat}
+ *
+ * @param width The width of the stream.
+ * @param height The height of the stream.
+ * @param format The {@link ImageFormat} of the stream.
+ */
+ public VirtualCameraStreamConfig(
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @ImageFormat.Format int format) {
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mFormat = format;
+ }
+
+ private VirtualCameraStreamConfig(@NonNull Parcel in) {
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mFormat = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mFormat);
+ }
+
+ @NonNull
+ public static final Creator<VirtualCameraStreamConfig> CREATOR =
+ new Creator<>() {
+ @Override
+ public VirtualCameraStreamConfig createFromParcel(Parcel in) {
+ return new VirtualCameraStreamConfig(in);
+ }
+
+ @Override
+ public VirtualCameraStreamConfig[] newArray(int size) {
+ return new VirtualCameraStreamConfig[size];
+ }
+ };
+
+ /** Returns the width of this stream. */
+ @IntRange(from = 1)
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /** Returns the height of this stream. */
+ @IntRange(from = 1)
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /** Returns the {@link ImageFormat} of this stream. */
+ @ImageFormat.Format
+ public int getFormat() {
+ return mFormat;
+ }
+}
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index cfab9ebec593..02066fa8a34e 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -58,6 +58,13 @@ flag {
}
flag {
+ name: "persistent_device_id_api"
+ namespace: "virtual_devices"
+ description: "Enable persistent device ID notification API"
+ bug: "295258915"
+}
+
+flag {
name: "express_metrics"
namespace: "virtual_devices"
description: "Enable express metrics in VDM"
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 0bc459a19e7d..67759f4aa76d 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -163,6 +163,7 @@ import java.util.List;
* into an editor), then {@link Item#coerceToText(Context)} will ask the content
* provider for the clip URI as text and successfully paste the entire note.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipData implements Parcelable {
static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN };
@@ -387,6 +388,7 @@ public class ClipData implements Parcelable {
* @return Returns the item's textual representation.
*/
//BEGIN_INCLUDE(coerceToText)
+ @android.ravenwood.annotation.RavenwoodThrow
public CharSequence coerceToText(Context context) {
// If this Item has an explicit textual value, simply return that.
CharSequence text = getText();
@@ -470,6 +472,7 @@ public class ClipData implements Parcelable {
* and other things can be retrieved.
* @return Returns the item's textual representation.
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public CharSequence coerceToStyledText(Context context) {
CharSequence text = getText();
if (text instanceof Spanned) {
@@ -520,6 +523,7 @@ public class ClipData implements Parcelable {
* and other things can be retrieved.
* @return Returns the item's representation as HTML text.
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public String coerceToHtmlText(Context context) {
// If the item has an explicit HTML value, simply return that.
String htmlText = getHtmlText();
@@ -540,6 +544,7 @@ public class ClipData implements Parcelable {
return text != null ? text.toString() : null;
}
+ @android.ravenwood.annotation.RavenwoodThrow
private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
// If this Item has a URI value, try using that.
if (mUri != null) {
@@ -1030,6 +1035,7 @@ public class ClipData implements Parcelable {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage) {
// Assume that callers are going to be granting permissions
prepareToLeaveProcess(leavingPackage, Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -1040,6 +1046,7 @@ public class ClipData implements Parcelable {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage, int intentFlags) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1060,6 +1067,7 @@ public class ClipData implements Parcelable {
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(AttributionSource source) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1073,6 +1081,7 @@ public class ClipData implements Parcelable {
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUris(int contentUserHint) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1090,6 +1099,7 @@ public class ClipData implements Parcelable {
* Only fixing the data field of the intents
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUrisLight(int contentUserHint) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index de2ba44ca393..5953890ad85f 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -48,6 +48,7 @@ import java.util.Map;
* developer guide.</p>
* </div>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipDescription implements Parcelable {
/**
* The MIME type for a clip holding plain text.
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index f12e971afb1f..a6a6bccbb4b5 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -37,6 +37,7 @@ import java.io.PrintWriter;
* name inside of that package.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
private final String mPackage;
private final String mClass;
diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java
index 767d3f668313..093faff654ec 100644
--- a/core/java/android/content/ContentUris.java
+++ b/core/java/android/content/ContentUris.java
@@ -70,6 +70,7 @@ import java.util.List;
*</dl>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ContentUris {
/**
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 02a5ba13f2aa..bde2f3e9a707 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -35,6 +35,7 @@ import java.util.Set;
* This class is used to store a set of values that the {@link ContentResolver}
* can process.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ContentValues implements Parcelable {
public static final String TAG = "ContentValues";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c917ee335af..1c6c7b5baa58 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7560,7 +7560,7 @@ public abstract class Context {
* @throws UnsupportedOperationException if the method is called on an instance that is not
* associated with any display.
*/
- @Nullable
+ @NonNull
public Display getDisplay() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ea54c912d4b9..c7a86fbe0171 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -660,6 +660,7 @@ import java.util.TimeZone;
* {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list
* of all possible flags.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Intent implements Parcelable, Cloneable {
private static final String TAG = "Intent";
@@ -2800,6 +2801,12 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast Action: An application package that was previously in the stopped state has been
* started and is no longer considered stopped.
+ * <p>When a package is force-stopped, the {@link #ACTION_PACKAGE_RESTARTED} broadcast is sent
+ * and the package in the stopped state cannot self-start for any reason unless there's an
+ * explicit request to start a component in the package. The {@link #ACTION_PACKAGE_UNSTOPPED}
+ * broadcast is sent when such an explicit process start occurs and the package is taken
+ * out of the stopped state.
+ * </p>
* <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
* <li> {@link #EXTRA_TIME} containing the {@link SystemClock#elapsedRealtime()
@@ -2807,6 +2814,9 @@ public class Intent implements Parcelable, Cloneable {
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
+ *
+ * @see ApplicationInfo#FLAG_STOPPED
+ * @see #ACTION_PACKAGE_RESTARTED
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
@@ -12171,6 +12181,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void prepareToLeaveProcess(Context context) {
final boolean leavingPackage;
@@ -12192,6 +12203,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage) {
setAllowFds(false);
@@ -12287,6 +12299,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(boolean fromProtectedComponent, AttributionSource source) {
if (fromProtectedComponent) {
prepareToEnterProcess(LOCAL_FLAG_FROM_PROTECTED_COMPONENT, source);
@@ -12298,6 +12311,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(int localFlags, AttributionSource source) {
// We just entered destination process, so we should be able to read all
// parcelables inside.
@@ -12369,6 +12383,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUris(int contentUserHint) {
Uri data = getData();
if (data != null) {
@@ -12408,6 +12423,7 @@ public class Intent implements Parcelable, Cloneable {
* @return Whether any contents were migrated.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean migrateExtraStreamToClipData() {
return migrateExtraStreamToClipData(AppGlobals.getInitialApplication());
}
@@ -12421,6 +12437,7 @@ public class Intent implements Parcelable, Cloneable {
* @return Whether any contents were migrated.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean migrateExtraStreamToClipData(Context context) {
// Refuse to touch if extras already parcelled
if (mExtras != null && mExtras.isParcelled()) return false;
@@ -12536,6 +12553,7 @@ public class Intent implements Parcelable, Cloneable {
return false;
}
+ @android.ravenwood.annotation.RavenwoodThrow
private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
&& context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.R) {
@@ -12587,8 +12605,13 @@ public class Intent implements Parcelable, Cloneable {
return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
}
- // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal}
- /** @hide */
+ /**
+ * @deprecated Use {@link SdkSandboxActivityAuthority#isSdkSandboxActivity} instead.
+ * Once the other API is finalized this method will be removed.
+ * @hide
+ */
+ @Deprecated
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean isSandboxActivity(@NonNull Context context) {
if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
return true;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index f946754bd9a1..ad3acd713c6b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -152,6 +152,7 @@ import java.util.function.Predicate;
* that unlike the action, an IntentFilter with no categories
* will only match an Intent that does not have any categories.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class IntentFilter implements Parcelable {
private static final String TAG = "IntentFilter";
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index addede4cc60c..a2cfbf5aa5bd 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -57,5 +57,11 @@
],
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsContentTestCasesRavenwood",
+ "host": true
+ }
]
-} \ No newline at end of file
+}
diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java
index 7fa48f0e9a78..4422ade7ec0a 100644
--- a/core/java/android/content/UriMatcher.java
+++ b/core/java/android/content/UriMatcher.java
@@ -119,6 +119,7 @@ instead of:
}
</pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class UriMatcher
{
public static final int NO_MATCH = -1;
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 20967274d0e4..072ffd797f7d 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -139,6 +139,13 @@ public final class Checksum implements Parcelable {
public @interface TypeMask {}
/**
+ * Max size of checksum in bytes.
+ * sizeof(SHA512) == 64 bytes
+ * @hide
+ */
+ public static final int MAX_CHECKSUM_SIZE_BYTES = 64;
+
+ /**
* Serialize checksum to the stream in binary format.
* @hide
*/
@@ -276,10 +283,10 @@ public final class Checksum implements Parcelable {
};
@DataClass.Generated(
- time = 1619810358402L,
+ time = 1700002689652L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
- inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
+ inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\npublic static final int MAX_CHECKSUM_SIZE_BYTES\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index a4d532712cfe..cb3455b266cd 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -22,11 +22,18 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.graphics.Paint;
import android.graphics.drawable.Drawable;
+import android.icu.text.UnicodeSet;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
/**
* A representation of an activity that can belong to this user or a managed
* profile associated with this user. It can be used to query the label, icon
@@ -36,6 +43,10 @@ public class LauncherActivityInfo {
private final PackageManager mPm;
private final LauncherActivityInfoInternal mInternal;
+ private static final UnicodeSet TRIMMABLE_CHARACTERS =
+ new UnicodeSet("[[:White_Space:][:Default_Ignorable_Code_Point:][:gc=Cc:]]",
+ /* ignoreWhitespace= */ false).freeze();
+
/**
* Create a launchable activity object for a given ResolveInfo and user.
*
@@ -77,8 +88,22 @@ public class LauncherActivityInfo {
* @return The label for the activity.
*/
public CharSequence getLabel() {
+ if (!Flags.lightweightInvisibleLabelDetection()) {
+ // TODO: Go through LauncherAppsService
+ return getActivityInfo().loadLabel(mPm);
+ }
+
+ CharSequence label = trim(getActivityInfo().loadLabel(mPm));
+ // If the trimmed label is empty, use application's label instead
+ if (TextUtils.isEmpty(label)) {
+ label = trim(getApplicationInfo().loadLabel(mPm));
+ // If the trimmed label is still empty, use package name instead
+ if (TextUtils.isEmpty(label)) {
+ label = getComponentName().getPackageName();
+ }
+ }
// TODO: Go through LauncherAppsService
- return getActivityInfo().loadLabel(mPm);
+ return label;
}
/**
@@ -180,4 +205,149 @@ public class LauncherActivityInfo {
return mPm.getUserBadgedIcon(originalIcon, mInternal.getUser());
}
+
+ /**
+ * If the {@code ch} is trimmable, return {@code true}. Otherwise, return
+ * {@code false}. If the count of the code points of {@code ch} doesn't
+ * equal 1, return {@code false}.
+ * <p>
+ * There are two types of the trimmable characters.
+ * 1. The character is one of the Default_Ignorable_Code_Point in
+ * <a href="
+ * https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt">
+ * DerivedCoreProperties.txt</a>, the White_Space in <a href=
+ * "https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt">PropList.txt
+ * </a> or category Cc.
+ * <p>
+ * 2. The character is not supported in the current system font.
+ * {@link android.graphics.Paint#hasGlyph(String)}
+ * <p>
+ *
+ */
+ private static boolean isTrimmable(@NonNull Paint paint, @NonNull CharSequence ch) {
+ Objects.requireNonNull(paint);
+ Objects.requireNonNull(ch);
+
+ // if ch is empty or it is not a character (i,e, the count of code
+ // point doesn't equal one), return false
+ if (TextUtils.isEmpty(ch)
+ || Character.codePointCount(ch, /* beginIndex= */ 0, ch.length()) != 1) {
+ return false;
+ }
+
+ // Return true for the cases as below:
+ // 1. The character is in the TRIMMABLE_CHARACTERS set
+ // 2. The character is not supported in the system font
+ return TRIMMABLE_CHARACTERS.contains(ch) || !paint.hasGlyph(ch.toString());
+ }
+
+ /**
+ * If the {@code sequence} has some leading trimmable characters, creates a new copy
+ * and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed string or the original string that has no
+ * leading trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trim(CharSequence)
+ * @see #trimEnd(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trimStart(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ final Paint paint = new Paint();
+ int trimCount = 0;
+ final int[] codePoints = sequence.codePoints().toArray();
+ for (int i = 0, length = codePoints.length; i < length; i++) {
+ String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
+ if (!isTrimmable(paint, ch)) {
+ break;
+ }
+ trimCount += ch.length();
+ }
+ if (trimCount == 0) {
+ return sequence;
+ }
+ return sequence.subSequence(trimCount, sequence.length());
+ }
+
+ /**
+ * If the {@code sequence} has some trailing trimmable characters, creates a new copy
+ * and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed sequence or the original sequence that has no
+ * trailing trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trimStart(CharSequence)
+ * @see #trim(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trimEnd(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ final Paint paint = new Paint();
+ int trimCount = 0;
+ final int[] codePoints = sequence.codePoints().toArray();
+ for (int i = codePoints.length - 1; i >= 0; i--) {
+ String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
+ if (!isTrimmable(paint, ch)) {
+ break;
+ }
+ trimCount += ch.length();
+ }
+
+ if (trimCount == 0) {
+ return sequence;
+ }
+ return sequence.subSequence(0, sequence.length() - trimCount);
+ }
+
+ /**
+ * If the {@code sequence} has some leading or trailing trimmable characters, creates
+ * a new copy and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed sequence or the original sequence that has no leading or
+ * trailing trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trimStart(CharSequence)
+ * @see #trimEnd(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trim(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ CharSequence result = trimStart(sequence);
+ if (TextUtils.isEmpty(result)) {
+ return result;
+ }
+
+ return trimEnd(result);
+ }
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 63c11b779641..1cfdb8b37fcd 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -16,8 +16,11 @@
package android.content.pm;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -244,6 +247,14 @@ public class PackageInfo implements Parcelable {
public Attribution[] attributions;
/**
+ * The time at which the app was archived for the user. Units are as
+ * per {@link System#currentTimeMillis()}.
+ * @hide
+ */
+ @CurrentTimeMillisLong
+ private long mArchiveTimeMillis;
+
+ /**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
@@ -508,6 +519,24 @@ public class PackageInfo implements Parcelable {
return overlayTarget != null && mOverlayIsStatic;
}
+ /**
+ * Returns the time at which the app was archived for the user. Units are as
+ * per {@link System#currentTimeMillis()}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public @CurrentTimeMillisLong long getArchiveTimeMillis() {
+ return mArchiveTimeMillis;
+ }
+
+ /**
+ * @hide
+ */
+ public void setArchiveTimeMillis(@CurrentTimeMillisLong long value) {
+ mArchiveTimeMillis = value;
+ }
+
@Override
public String toString() {
return "PackageInfo{"
@@ -575,6 +604,7 @@ public class PackageInfo implements Parcelable {
}
dest.writeBoolean(isApex);
dest.writeBoolean(isActiveApex);
+ dest.writeLong(mArchiveTimeMillis);
dest.restoreAllowSquashing(prevAllowSquashing);
}
@@ -640,5 +670,6 @@ public class PackageInfo implements Parcelable {
}
isApex = source.readBoolean();
isActiveApex = source.readBoolean();
+ mArchiveTimeMillis = source.readLong();
}
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6681e54beaa1..e9a2aaad6579 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -363,6 +363,19 @@ public class PackageInstaller {
"android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
/**
+ * Extra field for the unarchive ID. Sent as
+ * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
+ *
+ * @see Session#setUnarchiveId(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public static final String EXTRA_UNARCHIVE_ID =
+ "android.content.pm.extra.UNARCHIVE_ID";
+
+ /**
* If true, the requestor of the unarchival has specified that the app should be unarchived
* for {@link android.os.UserHandle#ALL}.
*
@@ -2268,6 +2281,8 @@ public class PackageInstaller {
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* visible to the caller or if the package has no
* installer on the device anymore to unarchive it.
+ * @throws IOException If parameters were unsatisfiable, such as lack of disk space.
+ *
* @hide
*/
@RequiresPermission(anyOf = {
@@ -2276,11 +2291,12 @@ public class PackageInstaller {
@SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void requestUnarchive(@NonNull String packageName)
- throws PackageManager.NameNotFoundException {
+ throws IOException, PackageManager.NameNotFoundException {
try {
mInstaller.requestUnarchive(packageName, mInstallerPackageName,
new UserHandle(mUserId));
} catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
e.maybeRethrow(PackageManager.NameNotFoundException.class);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2548,6 +2564,8 @@ public class PackageInstaller {
public boolean applicationEnabledSettingPersistent = false;
/** {@hide} */
public int developmentInstallFlags = 0;
+ /** {@hide} */
+ public int unarchiveId = -1;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2599,6 +2617,7 @@ public class PackageInstaller {
packageSource = source.readInt();
applicationEnabledSettingPersistent = source.readBoolean();
developmentInstallFlags = source.readInt();
+ unarchiveId = source.readInt();
}
/** {@hide} */
@@ -2632,6 +2651,7 @@ public class PackageInstaller {
ret.packageSource = packageSource;
ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
ret.developmentInstallFlags = developmentInstallFlags;
+ ret.unarchiveId = unarchiveId;
return ret;
}
@@ -3270,6 +3290,22 @@ public class PackageInstaller {
}
}
+ /**
+ * Used to set the unarchive ID received as part of an
+ * {@link Intent#ACTION_UNARCHIVE_PACKAGE}.
+ *
+ * <p> The ID should be retrieved from the unarchive intent and passed into the
+ * session that's being created to unarchive the app in question. Used to link the unarchive
+ * intent and the install session to disambiguate.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ @SystemApi
+ public void setUnarchiveId(int unarchiveId) {
+ this.unarchiveId = unarchiveId;
+ }
+
/** @hide */
@NonNull
public ArrayMap<String, Integer> getPermissionStates() {
@@ -3327,6 +3363,7 @@ public class PackageInstaller {
pw.printPair("applicationEnabledSettingPersistent",
applicationEnabledSettingPersistent);
pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
+ pw.printPair("unarchiveId", unarchiveId);
pw.println();
}
@@ -3370,6 +3407,7 @@ public class PackageInstaller {
dest.writeInt(packageSource);
dest.writeBoolean(applicationEnabledSettingPersistent);
dest.writeInt(developmentInstallFlags);
+ dest.writeInt(unarchiveId);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 72e106639544..c3b3423c1a57 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,6 +43,7 @@ import android.app.PackageInstallObserver;
import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
+import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -710,12 +711,31 @@ public abstract class PackageManager {
*/
@SystemApi
public interface OnPermissionsChangedListener {
-
/**
- * Called when the permissions for a UID change.
+ * Called when the permissions for a UID change for the default device.
+ *
* @param uid The UID with a change.
+ * @see Context#DEVICE_ID_DEFAULT
+ */
+ void onPermissionsChanged(int uid);
+
+ /**
+ * Called when the permissions for a UID change for a device, including virtual devices.
+ *
+ * @param uid The UID of permission change event.
+ * @param persistentDeviceId The persistent device ID of permission change event.
+ *
+ * @see VirtualDeviceManager.VirtualDevice#getPersistentDeviceId()
+ * @see VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT
*/
- public void onPermissionsChanged(int uid);
+ @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ default void onPermissionsChanged(int uid, @NonNull String persistentDeviceId) {
+ Objects.requireNonNull(persistentDeviceId);
+ if (Objects.equals(persistentDeviceId,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
+ onPermissionsChanged(uid);
+ }
+ }
}
/** @hide */
@@ -1481,6 +1501,7 @@ public abstract class PackageManager {
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
INSTALL_IGNORE_DEXOPT_PROFILE,
+ INSTALL_UNARCHIVE_DRAFT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1725,6 +1746,16 @@ public abstract class PackageManager {
public static final int INSTALL_IGNORE_DEXOPT_PROFILE = 1 << 28;
/**
+ * If set, then the session is a draft session created for an upcoming unarchival by its
+ * installer.
+ *
+ * @see PackageInstaller#requestUnarchive(String)
+ *
+ * @hide
+ */
+ public static final int INSTALL_UNARCHIVE_DRAFT = 1 << 29;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
@@ -4019,6 +4050,15 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports multiple concurrent IME sessions.
+ */
+ @FlaggedApi("android.view.inputmethod.concurrent_input_methods")
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CONCURRENT_INPUT_METHODS =
+ "android.software.concurrent_input_methods";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports device policy enforcement via device admins.
*/
@SdkConstant(SdkConstantType.FEATURE)
@@ -6364,9 +6404,8 @@ public abstract class PackageManager {
/**
* Permission flags set when granting or revoking a permission.
*
- * @hide
+ * @removed mistakenly exposed as system-api previously
*/
- @SystemApi
@IntDef(prefix = { "FLAG_PERMISSION_" }, value = {
FLAG_PERMISSION_USER_SET,
FLAG_PERMISSION_USER_FIXED,
@@ -9781,7 +9820,8 @@ public abstract class PackageManager {
* launcher to support customization that they might need to handle the suspended state.
*
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API except for
- * device owner and profile owner.
+ * device owner and profile owner or the {@link Manifest.permission#QUARANTINE_APPS} if the
+ * caller is using {@link #FLAG_SUSPEND_QUARANTINED}.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -9809,7 +9849,10 @@ public abstract class PackageManager {
*/
@SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
- @RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.SUSPEND_APPS,
+ Manifest.permission.QUARANTINE_APPS
+ }, conditional = true)
@SuppressLint("NullableCollection")
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index 543703e734c2..a40770484ca5 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -190,9 +190,11 @@ public final class SigningInfo implements Parcelable {
mSigningDetails.writeToParcel(dest, parcelableFlags);
}
- /* @hide */
+ /**
+ * @hide
+ */
@NonNull
- SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return mSigningDetails;
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 884d463e929d..f532c4cffcc4 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -62,6 +62,8 @@ public final class UserProperties implements Parcelable {
"mediaSharedWithParent";
private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
"credentialShareableWithParent";
+ private static final String ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE =
+ "authAlwaysRequiredToDisableQuietMode";
private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible";
@@ -80,6 +82,7 @@ public final class UserProperties implements Parcelable {
INDEX_DELETE_APP_WITH_PARENT,
INDEX_ALWAYS_VISIBLE,
INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE,
+ INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -97,6 +100,7 @@ public final class UserProperties implements Parcelable {
private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
private static final int INDEX_ALWAYS_VISIBLE = 11;
private static final int INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE = 12;
+ private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -329,6 +333,8 @@ public final class UserProperties implements Parcelable {
setShowInSettings(orig.getShowInSettings());
setHideInSettingsInQuietMode(orig.getHideInSettingsInQuietMode());
setUseParentsContacts(orig.getUseParentsContacts());
+ setAuthAlwaysRequiredToDisableQuietMode(
+ orig.isAuthAlwaysRequiredToDisableQuietMode());
}
if (hasQueryOrManagePermission) {
// Add items that require QUERY_USERS or stronger.
@@ -611,6 +617,31 @@ public final class UserProperties implements Parcelable {
}
private boolean mCredentialShareableWithParent;
+ /**
+ * Returns whether the profile always requires user authentication to disable from quiet mode.
+ *
+ * <p> Settings this field to true will ensure that the credential confirmation activity is
+ * always shown whenever the user requests to disable quiet mode. The behavior of credential
+ * checks is not guaranteed when the property is false and may vary depending on user types.
+ * @hide
+ */
+ public boolean isAuthAlwaysRequiredToDisableQuietMode() {
+ if (isPresent(INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE)) {
+ return mAuthAlwaysRequiredToDisableQuietMode;
+ }
+ if (mDefaultProperties != null) {
+ return mDefaultProperties.mAuthAlwaysRequiredToDisableQuietMode;
+ }
+ throw new SecurityException(
+ "You don't have permission to query authAlwaysRequiredToDisableQuietMode");
+ }
+ /** @hide */
+ public void setAuthAlwaysRequiredToDisableQuietMode(boolean val) {
+ this.mAuthAlwaysRequiredToDisableQuietMode = val;
+ setPresent(INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE);
+ }
+ private boolean mAuthAlwaysRequiredToDisableQuietMode;
+
/*
Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
OTA update between user-parent
@@ -693,6 +724,8 @@ public final class UserProperties implements Parcelable {
+ getCrossProfileIntentResolutionStrategy()
+ ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+ ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ + ", mAuthAlwaysRequiredToDisableQuietMode="
+ + isAuthAlwaysRequiredToDisableQuietMode()
+ ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ ", mAlwaysVisible=" + getAlwaysVisible()
+ "}";
@@ -720,6 +753,8 @@ public final class UserProperties implements Parcelable {
pw.println(prefix + " mMediaSharedWithParent=" + isMediaSharedWithParent());
pw.println(prefix + " mCredentialShareableWithParent="
+ isCredentialShareableWithParent());
+ pw.println(prefix + " mAuthAlwaysRequiredToDisableQuietMode="
+ + isAuthAlwaysRequiredToDisableQuietMode());
pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible());
}
@@ -788,6 +823,9 @@ public final class UserProperties implements Parcelable {
case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
setCredentialShareableWithParent(parser.getAttributeBoolean(i));
break;
+ case ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE:
+ setAuthAlwaysRequiredToDisableQuietMode(parser.getAttributeBoolean(i));
+ break;
case ATTR_DELETE_APP_WITH_PARENT:
setDeleteAppWithParent(parser.getAttributeBoolean(i));
break;
@@ -853,6 +891,10 @@ public final class UserProperties implements Parcelable {
serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
mCredentialShareableWithParent);
}
+ if (isPresent(INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE)) {
+ serializer.attributeBoolean(null, ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
+ mAuthAlwaysRequiredToDisableQuietMode);
+ }
if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
mDeleteAppWithParent);
@@ -878,6 +920,7 @@ public final class UserProperties implements Parcelable {
dest.writeInt(mCrossProfileIntentResolutionStrategy);
dest.writeBoolean(mMediaSharedWithParent);
dest.writeBoolean(mCredentialShareableWithParent);
+ dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode);
dest.writeBoolean(mDeleteAppWithParent);
dest.writeBoolean(mAlwaysVisible);
}
@@ -901,6 +944,7 @@ public final class UserProperties implements Parcelable {
mCrossProfileIntentResolutionStrategy = source.readInt();
mMediaSharedWithParent = source.readBoolean();
mCredentialShareableWithParent = source.readBoolean();
+ mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean();
mDeleteAppWithParent = source.readBoolean();
mAlwaysVisible = source.readBoolean();
}
@@ -941,6 +985,7 @@ public final class UserProperties implements Parcelable {
CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
private boolean mMediaSharedWithParent = false;
private boolean mCredentialShareableWithParent = false;
+ private boolean mAuthAlwaysRequiredToDisableQuietMode = false;
private boolean mDeleteAppWithParent = false;
private boolean mAlwaysVisible = false;
@@ -1010,6 +1055,14 @@ public final class UserProperties implements Parcelable {
return this;
}
+ /** Sets the value for {@link #mAuthAlwaysRequiredToDisableQuietMode} */
+ public Builder setAuthAlwaysRequiredToDisableQuietMode(
+ boolean authAlwaysRequiredToDisableQuietMode) {
+ mAuthAlwaysRequiredToDisableQuietMode =
+ authAlwaysRequiredToDisableQuietMode;
+ return this;
+ }
+
/** Sets the value for {@link #mDeleteAppWithParent}*/
public Builder setDeleteAppWithParent(boolean deleteAppWithParent) {
mDeleteAppWithParent = deleteAppWithParent;
@@ -1036,6 +1089,7 @@ public final class UserProperties implements Parcelable {
mCrossProfileIntentResolutionStrategy,
mMediaSharedWithParent,
mCredentialShareableWithParent,
+ mAuthAlwaysRequiredToDisableQuietMode,
mDeleteAppWithParent,
mAlwaysVisible);
}
@@ -1053,6 +1107,7 @@ public final class UserProperties implements Parcelable {
@CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
boolean mediaSharedWithParent,
boolean credentialShareableWithParent,
+ boolean authAlwaysRequiredToDisableQuietMode,
boolean deleteAppWithParent,
boolean alwaysVisible) {
mDefaultProperties = null;
@@ -1067,6 +1122,8 @@ public final class UserProperties implements Parcelable {
setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
setMediaSharedWithParent(mediaSharedWithParent);
setCredentialShareableWithParent(credentialShareableWithParent);
+ setAuthAlwaysRequiredToDisableQuietMode(
+ authAlwaysRequiredToDisableQuietMode);
setDeleteAppWithParent(deleteAppWithParent);
setAlwaysVisible(alwaysVisible);
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 814eae6726a9..bb5fdb714761 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -80,3 +80,10 @@ flag {
description: "Feature flag to retrieve resolved path of the base APK during an app install."
bug: "269728874"
}
+
+flag {
+ name: "lightweight_invisible_label_detection"
+ namespace: "package_manager_service"
+ description: "Feature flag to detect the invisible labels in Launcher Apps"
+ bug: "299586370"
+}
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index f3194be81b0d..4990a27c48f8 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -140,6 +140,11 @@ public class ApkLite {
private final boolean mIsSdkLibrary;
/**
+ * Indicates if this system app can be updated.
+ */
+ private final boolean mUpdatableSystem;
+
+ /**
* Archival install info.
*/
private final @Nullable ArchivedPackageParcel mArchivedPackage;
@@ -154,7 +159,7 @@ public class ApkLite {
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -188,6 +193,7 @@ public class ApkLite {
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
+ mUpdatableSystem = updatableSystem;
mArchivedPackage = null;
}
@@ -225,6 +231,7 @@ public class ApkLite {
mRollbackDataPolicy = 0;
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
+ mUpdatableSystem = true;
mArchivedPackage = archivedPackage;
}
@@ -535,6 +542,14 @@ public class ApkLite {
}
/**
+ * Indicates if this system app can be updated.
+ */
+ @DataClass.Generated.Member
+ public boolean isUpdatableSystem() {
+ return mUpdatableSystem;
+ }
+
+ /**
* Archival install info.
*/
@DataClass.Generated.Member
@@ -543,10 +558,10 @@ public class ApkLite {
}
@DataClass.Generated(
- time = 1694792109463L,
+ time = 1699587291575L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 5f86742fc562..462667926053 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -424,6 +424,7 @@ public class ApkLiteParseUtils {
0);
int revisionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "revisionCode", 0);
boolean coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+ boolean updatableSystem = parser.getAttributeBooleanValue(null, "updatableSystem", true);
boolean isolatedSplits = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"isolatedSplits", false);
boolean isFeatureSplit = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
@@ -505,14 +506,18 @@ public class ApkLiteParseUtils {
continue;
}
- if (TAG_PROFILEABLE.equals(parser.getName())) {
- profilableByShell = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
- "shell", profilableByShell);
- } else if (TAG_RECEIVER.equals(parser.getName())) {
- hasDeviceAdminReceiver |= isDeviceAdminReceiver(
- parser, hasBindDeviceAdminPermission);
- } else if (TAG_SDK_LIBRARY.equals(parser.getName())) {
- isSdkLibrary = true;
+ switch (parser.getName()) {
+ case TAG_PROFILEABLE:
+ profilableByShell = parser.getAttributeBooleanValue(
+ ANDROID_RES_NAMESPACE, "shell", profilableByShell);
+ break;
+ case TAG_RECEIVER:
+ hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
+ hasBindDeviceAdminPermission);
+ break;
+ case TAG_SDK_LIBRARY:
+ isSdkLibrary = true;
+ break;
}
}
} else if (TAG_OVERLAY.equals(parser.getName())) {
@@ -614,7 +619,7 @@ public class ApkLiteParseUtils {
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary));
+ hasDeviceAdminReceiver, isSdkLibrary, updatableSystem));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 62630c8909d3..d27479299efa 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -163,6 +163,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
GRAMMATICAL_GENDER_FEMININE,
GRAMMATICAL_GENDER_MASCULINE,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface GrammaticalGender {}
/**
@@ -698,6 +699,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
ORIENTATION_LANDSCAPE,
ORIENTATION_SQUARE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface Orientation {
}
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 1ef3d273fcb8..38dbec5a5529 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -38,9 +38,11 @@ public class Element {
private static final int MAX_ATTR_LEN_PERMISSION_GROUP = 256;
private static final int MAX_ATTR_LEN_PACKAGE = 256;
private static final int MAX_ATTR_LEN_MIMETYPE = 512;
- public static final int MAX_ATTR_LEN_NAME = 1024;
- public static final int MAX_ATTR_LEN_PATH = 4000;
- public static final int MAX_ATTR_LEN_DATA_VALUE = 4000;
+ private static final int MAX_ATTR_LEN_NAME = 1024;
+ private static final int MAX_ATTR_LEN_PATH = 4000;
+ private static final int MAX_ATTR_LEN_VALUE = 32_768;
+
+ private static final int MAX_TOTAL_META_DATA_SIZE = 262_144;
private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?%^*|/\\";
@@ -157,6 +159,7 @@ public class Element {
}
private long mChildTagMask = 0;
+ private int mTotalComponentMetadataSize = 0;
private static int getCounterIdx(String tag) {
switch(tag) {
@@ -283,6 +286,7 @@ public class Element {
private void init(String tag) {
this.mTag = tag;
mChildTagMask = 0;
+ mTotalComponentMetadataSize = 0;
switch (tag) {
case TAG_ACTIVITY:
initializeCounter(TAG_LAYOUT, 1000);
@@ -386,7 +390,7 @@ public class Element {
case TAG_ATTR_PATH_SUFFIX:
return MAX_ATTR_LEN_PATH;
case TAG_ATTR_VALUE:
- return MAX_ATTR_LEN_DATA_VALUE;
+ return MAX_ATTR_LEN_VALUE;
case TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_VALUE:
return PROP_VALUE_MAX;
default:
@@ -566,7 +570,7 @@ public class Element {
case R.styleable.AndroidManifestMetaData_name:
return MAX_ATTR_LEN_NAME;
case R.styleable.AndroidManifestMetaData_value:
- return MAX_ATTR_LEN_DATA_VALUE;
+ return MAX_ATTR_LEN_VALUE;
default:
return DEFAULT_MAX_STRING_ATTR_LENGTH;
}
@@ -636,7 +640,7 @@ public class Element {
case R.styleable.AndroidManifestProperty_name:
return MAX_ATTR_LEN_NAME;
case R.styleable.AndroidManifestProperty_value:
- return MAX_ATTR_LEN_DATA_VALUE;
+ return MAX_ATTR_LEN_VALUE;
default:
return DEFAULT_MAX_STRING_ATTR_LENGTH;
}
@@ -820,6 +824,12 @@ public class Element {
}
}
+ void validateComponentMetadata(String value) {
+ mTotalComponentMetadataSize += value.length();
+ if (mTotalComponentMetadataSize > MAX_TOTAL_META_DATA_SIZE) {
+ throw new SecurityException("Max total meta data size limit exceeded for " + mTag);
+ }
+ }
void seen(@NonNull Element element) {
TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
index cae353b3bd5a..3b68452b1a5c 100644
--- a/core/java/android/content/res/Validator.java
+++ b/core/java/android/content/res/Validator.java
@@ -19,6 +19,8 @@ package android.content.res;
import android.annotation.NonNull;
import android.annotation.StyleableRes;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,6 +86,9 @@ public class Validator {
return;
}
mElements.peek().validateResStrAttr(index, stringValue);
+ if (index == R.styleable.AndroidManifestMetaData_value) {
+ validateComponentMetadata(stringValue.toString());
+ }
}
/**
@@ -94,5 +99,20 @@ public class Validator {
return;
}
mElements.peek().validateStrAttr(attrName, attrValue);
+ if (attrName.equals(Element.TAG_ATTR_VALUE)) {
+ validateComponentMetadata(attrValue);
+ }
+ }
+
+ private void validateComponentMetadata(String attrValue) {
+ Element element = mElements.peek();
+ // Meta-data values are evaluated on the parent element which is the next element in the
+ // mElements stack after the meta-data element. The top of the stack is always the current
+ // element being validated so check that the top element is meta-data.
+ if (element.mTag.equals(Element.TAG_META_DATA) && mElements.size() > 1) {
+ element = mElements.pop();
+ mElements.peek().validateComponentMetadata(attrValue);
+ mElements.push(element);
+ }
}
}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index ec96215525d3..bab84aadc73b 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -20,3 +20,10 @@ flag {
description: "Enables clearing of Credential Manager sessions when client process dies"
bug: "308470501"
}
+
+flag {
+ namespace: "credential_manager"
+ name: "new_settings_intents"
+ description: "Enables settings intents to redirect to new settings page"
+ bug: "307587989"
+} \ No newline at end of file
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 69d573f84975..80f085f5b9fe 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -33,11 +33,11 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
-
/**
* This is an abstract cursor class that handles a lot of the common code
* that all cursors need to deal with and is provided for convenience reasons.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class AbstractCursor implements CrossProcessCursor {
private static final String TAG = "Cursor";
@@ -89,7 +89,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
private Bundle mExtras = Bundle.EMPTY;
/** CloseGuard to detect leaked cursor **/
- private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final CloseGuard mCloseGuard;
/* -------------------------------------------------------- */
/* These need to be implemented by subclasses */
@@ -184,7 +184,9 @@ public abstract class AbstractCursor implements CrossProcessCursor {
mClosed = true;
mContentObservable.unregisterAll();
onDeactivateOrClose();
- mCloseGuard.close();
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
}
/**
@@ -224,7 +226,19 @@ public abstract class AbstractCursor implements CrossProcessCursor {
/* Implementation */
public AbstractCursor() {
mPos = -1;
- mCloseGuard.open("AbstractCursor.close");
+ mCloseGuard = initCloseGuard();
+ if (mCloseGuard != null) {
+ mCloseGuard.open("AbstractCursor.close");
+ }
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private CloseGuard initCloseGuard() {
+ return CloseGuard.get();
+ }
+
+ private CloseGuard initCloseGuard$ravenwood() {
+ return null;
}
@Override
diff --git a/core/java/android/database/CharArrayBuffer.java b/core/java/android/database/CharArrayBuffer.java
index 73781b763c3d..4927654c38b9 100644
--- a/core/java/android/database/CharArrayBuffer.java
+++ b/core/java/android/database/CharArrayBuffer.java
@@ -19,6 +19,7 @@ package android.database;
/**
* This is used for {@link Cursor#copyStringToBuffer}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CharArrayBuffer {
public CharArrayBuffer(int size) {
data = new char[size];
diff --git a/core/java/android/database/ContentObservable.java b/core/java/android/database/ContentObservable.java
index 7692bb39da71..dc35b5af2a98 100644
--- a/core/java/android/database/ContentObservable.java
+++ b/core/java/android/database/ContentObservable.java
@@ -23,6 +23,7 @@ import android.net.Uri;
* that provides methods for sending notifications to a list of
* {@link ContentObserver} objects.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ContentObservable extends Observable<ContentObserver> {
// Even though the generic method defined in Observable would be perfectly
// fine on its own, we can't delete this overridden method because it would
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index c27ee51b9315..39c9400e7064 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -36,6 +36,7 @@ import java.util.Collection;
* Receives call backs for changes to content.
* Must be implemented by objects which are added to a {@link ContentObservable}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ContentObserver {
/**
* Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new
@@ -49,7 +50,6 @@ public abstract class ContentObserver {
@ChangeId
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L;
-
private final Object mLock = new Object();
private Transport mTransport; // guarded by mLock
@@ -216,7 +216,7 @@ public abstract class ContentObserver {
// There are dozens of people relying on the hidden API inside the
// system UID, so hard-code the old behavior for all of them; for
// everyone else we gate based on a specific change
- if (!CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS)
+ if (!isChangeEnabledAddContentObserverFlags()
|| android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
// Deliver userId through argument to preserve hidden API behavior
onChange(selfChange, uris, flags, UserHandle.of(userId));
@@ -225,6 +225,15 @@ public abstract class ContentObserver {
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean isChangeEnabledAddContentObserverFlags() {
+ return CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS);
+ }
+
+ private static boolean isChangeEnabledAddContentObserverFlags$ravenwood() {
+ return true;
+ }
+
/**
* Dispatches a change notification to the observer.
* <p>
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index afa1c209c811..cb1d3f5252b2 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -40,6 +40,7 @@ import java.util.List;
* Implementations should subclass {@link AbstractCursor}.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Cursor extends Closeable {
/*
* Values returned by {@link #getType(int)}.
diff --git a/core/java/android/database/CursorIndexOutOfBoundsException.java b/core/java/android/database/CursorIndexOutOfBoundsException.java
index 1f77d0047191..89d44182a886 100644
--- a/core/java/android/database/CursorIndexOutOfBoundsException.java
+++ b/core/java/android/database/CursorIndexOutOfBoundsException.java
@@ -19,6 +19,7 @@ package android.database;
/**
* An exception indicating that a cursor is out of bounds.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CursorIndexOutOfBoundsException extends IndexOutOfBoundsException {
public CursorIndexOutOfBoundsException(int index, int size) {
diff --git a/core/java/android/database/CursorJoiner.java b/core/java/android/database/CursorJoiner.java
index a95263b67b43..2eb81a1eed5e 100644
--- a/core/java/android/database/CursorJoiner.java
+++ b/core/java/android/database/CursorJoiner.java
@@ -42,6 +42,7 @@ import java.util.Iterator;
* }
* </pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CursorJoiner
implements Iterator<CursorJoiner.Result>, Iterable<CursorJoiner.Result> {
private Cursor mCursorLeft;
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index 4496f805cc2e..6572f99cdd07 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -27,6 +27,7 @@ import java.util.List;
* Wrapper class for Cursor that delegates all calls to the actual cursor object. The primary
* use for this class is to extend a cursor while overriding only a subset of its methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CursorWrapper implements Cursor {
/** @hide */
@UnsupportedAppUsage
diff --git a/core/java/android/database/DataSetObservable.java b/core/java/android/database/DataSetObservable.java
index ca77a13c2352..ff47f9a53a9a 100644
--- a/core/java/android/database/DataSetObservable.java
+++ b/core/java/android/database/DataSetObservable.java
@@ -21,6 +21,7 @@ package android.database;
* that provides methods for sending notifications to a list of
* {@link DataSetObserver} objects.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
diff --git a/core/java/android/database/DataSetObserver.java b/core/java/android/database/DataSetObserver.java
index 28616c80bd5a..13469cbf2645 100644
--- a/core/java/android/database/DataSetObserver.java
+++ b/core/java/android/database/DataSetObserver.java
@@ -21,6 +21,7 @@ package android.database;
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index 050a49ac959e..ad35b2f96eab 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
* {@link #newRow()} to add rows. Automatically expands internal capacity
* as needed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MatrixCursor extends AbstractCursor {
private final String[] columnNames;
diff --git a/core/java/android/database/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 272cfa24181c..5a5675694040 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -22,6 +22,7 @@ package android.database;
* may be different if that is desired. Calls to getColumns, getColumnIndex, etc will return the
* value for the row that the MergeCursor is currently pointing at.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MergeCursor extends AbstractCursor
{
private DataSetObserver mObserver = new DataSetObserver() {
diff --git a/core/java/android/database/Observable.java b/core/java/android/database/Observable.java
index aff32db1bf9f..a3057aca7936 100644
--- a/core/java/android/database/Observable.java
+++ b/core/java/android/database/Observable.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
*
* @param T The observer type.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 33f602bbd40e..c59d3cea0414 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -163,7 +163,7 @@ public final class SQLiteRawStatement implements Closeable {
* {@link IllegalStateException} if a transaction is not in progress. Clients should call
* {@link SQLiteDatabase.createRawStatement} to create a new instance.
*/
- SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) throws SQLiteException {
+ SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) {
mThread = Thread.currentThread();
mDatabase = db;
mSession = mDatabase.getThreadSession();
@@ -245,7 +245,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteDatabaseLockedException if the database is locked or busy.
* @throws SQLiteException if a native error occurs.
*/
- public boolean step() throws SQLiteException {
+ public boolean step() {
throwIfInvalid();
try {
int err = nativeStep(mStatement, true);
@@ -392,7 +392,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value) throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -418,8 +418,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length)
- throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length) {
Objects.requireNonNull(value);
throwIfInvalid();
throwIfInvalidBounds(value.length, offset, length);
@@ -442,7 +441,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindDouble(int parameterIndex, double value) throws SQLiteException {
+ public void bindDouble(int parameterIndex, double value) {
throwIfInvalid();
try {
nativeBindDouble(mStatement, parameterIndex, value);
@@ -462,7 +461,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindInt(int parameterIndex, int value) throws SQLiteException {
+ public void bindInt(int parameterIndex, int value) {
throwIfInvalid();
try {
nativeBindInt(mStatement, parameterIndex, value);
@@ -482,7 +481,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindLong(int parameterIndex, long value) throws SQLiteException {
+ public void bindLong(int parameterIndex, long value) {
throwIfInvalid();
try {
nativeBindLong(mStatement, parameterIndex, value);
@@ -502,7 +501,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindNull(int parameterIndex) throws SQLiteException {
+ public void bindNull(int parameterIndex) {
throwIfInvalid();
try {
nativeBindNull(mStatement, parameterIndex);
@@ -523,7 +522,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindText(int parameterIndex, @NonNull String value) throws SQLiteException {
+ public void bindText(int parameterIndex, @NonNull String value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -562,7 +561,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteException if a native error occurs.
*/
@SQLiteDataType
- public int getColumnType(int columnIndex) throws SQLiteException {
+ public int getColumnType(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnType(mStatement, columnIndex);
@@ -584,7 +583,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
*/
@NonNull
- public String getColumnName(int columnIndex) throws SQLiteException {
+ public String getColumnName(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnName(mStatement, columnIndex);
@@ -609,7 +608,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnLength(int columnIndex) throws SQLiteException {
+ public int getColumnLength(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBytes(mStatement, columnIndex);
@@ -635,7 +634,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteException if a native error occurs.
*/
@Nullable
- public byte[] getColumnBlob(int columnIndex) throws SQLiteException {
+ public byte[] getColumnBlob(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBlob(mStatement, columnIndex);
@@ -668,8 +667,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteException if a native error occurs.
*/
public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
- int length, int srcOffset)
- throws SQLiteException {
+ int length, int srcOffset) {
Objects.requireNonNull(buffer);
throwIfInvalid();
throwIfInvalidBounds(buffer.length, offset, length);
@@ -695,7 +693,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public double getColumnDouble(int columnIndex) throws SQLiteException {
+ public double getColumnDouble(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnDouble(mStatement, columnIndex);
@@ -719,7 +717,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnInt(int columnIndex) throws SQLiteException {
+ public int getColumnInt(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnInt(mStatement, columnIndex);
@@ -743,7 +741,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public long getColumnLong(int columnIndex) throws SQLiteException {
+ public long getColumnLong(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnLong(mStatement, columnIndex);
@@ -768,7 +766,7 @@ public final class SQLiteRawStatement implements Closeable {
* @throws SQLiteException if a native error occurs.
*/
@NonNull
- public String getColumnText(int columnIndex) throws SQLiteException {
+ public String getColumnText(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnText(mStatement, columnIndex);
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index acdc0fac0e98..d33eadcfd11b 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -27,7 +27,6 @@ import android.os.ParcelFileDescriptor;
* <p>
* This class is not thread-safe.
* </p>
- * Note that this class is unrelated to {@link SQLiteRawStatement}.
*/
public final class SQLiteStatement extends SQLiteProgram {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 90bbca8336e1..f82f79eba8c9 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -131,6 +131,7 @@ public class BiometricManager {
BIOMETRIC_CONVENIENCE,
DEVICE_CREDENTIAL,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface Types {}
/**
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index ea951a55bfca..0a61c32a9cf5 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -736,6 +736,9 @@ public final class CameraExtensionCharacteristics {
return generateJpegSupportedSizes(
extenders.second.getSupportedPostviewResolutions(sz),
streamMap);
+ } else if (format == ImageFormat.JPEG_R) {
+ // Jpeg_R/UltraHDR is currently not supported in the basic extension case
+ return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
@@ -858,6 +861,7 @@ public final class CameraExtensionCharacteristics {
switch(format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
+ case ImageFormat.JPEG_R:
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
@@ -890,6 +894,9 @@ public final class CameraExtensionCharacteristics {
} else {
return generateSupportedSizes(null, format, streamMap);
}
+ } else if (format == ImageFormat.JPEG_R) {
+ // Jpeg_R/UltraHDR is currently not supported in the basic extension case
+ return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index aca6d0646a9b..5d0697806512 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3402,8 +3402,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -3464,6 +3464,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1c66f82767e6..0d204f3ececa 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4103,8 +4103,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -4165,6 +4165,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
index f4fc472accbe..a8066aa74f95 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -47,7 +47,8 @@ public final class CameraExtensionUtils {
public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
- ImageFormat.JPEG
+ ImageFormat.JPEG,
+ ImageFormat.JPEG_R
};
public static class SurfaceInfo {
@@ -92,6 +93,10 @@ public final class CameraExtensionUtils {
(dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
surfaceInfo.mFormat = ImageFormat.JPEG;
return surfaceInfo;
+ } else if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB)
+ && (dataspace == StreamConfigurationMap.HAL_DATASPACE_JPEG_R)) {
+ surfaceInfo.mFormat = ImageFormat.JPEG_R;
+ return surfaceInfo;
}
return surfaceInfo;
diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
index 8304796f636a..cf496d2ec88e 100644
--- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
+++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
@@ -90,6 +90,22 @@ public class FrameNumberTracker {
break;
}
}
+
+ if (!removeError) {
+ // Check for the case where we might have an error after a frame number gap
+ // caused by other types of capture requests
+ int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
+ int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
+ if (mPendingFrameNumbersWithOtherType[otherType1].isEmpty() &&
+ mPendingFrameNumbersWithOtherType[otherType2].isEmpty()) {
+ long errorGapNumber = Math.max(mCompletedFrameNumber[otherType1],
+ mCompletedFrameNumber[otherType2]) + 1;
+ if ((errorGapNumber > mCompletedFrameNumber[requestType] + 1) &&
+ (errorGapNumber == errorFrameNumber)) {
+ removeError = true;
+ }
+ }
+ }
}
if (removeError) {
mCompletedFrameNumber[requestType] = errorFrameNumber;
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4791a8341912..f71e853a1170 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -721,8 +721,19 @@ public abstract class DisplayManagerInternal {
public interface DisplayOffloadSession {
/** Provide the display state to use in place of state DOZE. */
void setDozeStateOverride(int displayState);
- /** Returns the associated DisplayOffloader. */
- DisplayOffloader getDisplayOffloader();
+
+ /** Whether the session is active. */
+ boolean isActive();
+
+ /**
+ * Update the brightness from the offload chip.
+ * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
+ * {@link PowerManager.BRIGHTNESS_MAX}, or
+ * {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} which removes
+ * the brightness from offload. Other values will be ignored.
+ */
+ void updateBrightness(float brightness);
+
/** Returns whether displayoffload supports the given display state. */
static boolean isSupportedOffloadState(int displayState) {
return Display.isSuspendedState(displayState);
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 22e3938d3818..7388b5bb1495 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,10 +18,12 @@ package android.hardware.display;
import static android.view.Display.DEFAULT_DISPLAY;
+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.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.media.projection.MediaProjection;
import android.os.Handler;
@@ -55,6 +57,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private final boolean mWindowManagerMirroringEnabled;
private ArraySet<String> mDisplayCategories = null;
private final float mRequestedRefreshRate;
+ private final boolean mIsHomeSupported;
private VirtualDisplayConfig(
@NonNull String name,
@@ -67,7 +70,8 @@ public final class VirtualDisplayConfig implements Parcelable {
int displayIdToMirror,
boolean windowManagerMirroringEnabled,
@NonNull ArraySet<String> displayCategories,
- float requestedRefreshRate) {
+ float requestedRefreshRate,
+ boolean isHomeSupported) {
mName = name;
mWidth = width;
mHeight = height;
@@ -79,6 +83,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
+ mIsHomeSupported = isHomeSupported;
}
/**
@@ -157,6 +162,18 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Whether this virtual display supports showing home activity and wallpaper.
+ *
+ * @see Builder#setHomeSupported
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+ @SystemApi
+ public boolean isHomeSupported() {
+ return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+ }
+
+ /**
* Returns the display categories.
*
* @see Builder#setDisplayCategories
@@ -189,6 +206,7 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeBoolean(mWindowManagerMirroringEnabled);
dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
+ dest.writeBoolean(mIsHomeSupported);
}
@Override
@@ -213,7 +231,8 @@ public final class VirtualDisplayConfig implements Parcelable {
&& mDisplayIdToMirror == that.mDisplayIdToMirror
&& mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
- && mRequestedRefreshRate == that.mRequestedRefreshRate;
+ && mRequestedRefreshRate == that.mRequestedRefreshRate
+ && mIsHomeSupported == that.mIsHomeSupported;
}
@Override
@@ -221,7 +240,7 @@ public final class VirtualDisplayConfig implements Parcelable {
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
- mRequestedRefreshRate);
+ mRequestedRefreshRate, mIsHomeSupported);
return hashCode;
}
@@ -240,6 +259,7 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ + " mIsHomeSupported=" + mIsHomeSupported
+ ")";
}
@@ -255,6 +275,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mWindowManagerMirroringEnabled = in.readBoolean();
mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
+ mIsHomeSupported = in.readBoolean();
}
@NonNull
@@ -286,6 +307,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private boolean mWindowManagerMirroringEnabled = false;
private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
+ private boolean mIsHomeSupported = false;
/**
* Creates a new Builder.
@@ -422,6 +444,27 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Sets whether this display supports showing home activities and wallpaper.
+ *
+ * <p>If set to {@code true}, then the home activity relevant to this display will be
+ * automatically launched upon the display creation.</p>
+ *
+ * <p>Note: setting to {@code true} requires the display to be trusted. If the display is
+ * not trusted, this property is ignored.</p>
+ *
+ * @param isHomeSupported whether home activities are supported on the display
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+ @SystemApi
+ @NonNull
+ public Builder setHomeSupported(boolean isHomeSupported) {
+ mIsHomeSupported = isHomeSupported;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -437,7 +480,8 @@ public final class VirtualDisplayConfig implements Parcelable {
mDisplayIdToMirror,
mWindowManagerMirroringEnabled,
mDisplayCategories,
- mRequestedRefreshRate);
+ mRequestedRefreshRate,
+ mIsHomeSupported);
}
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b0b7a416c019..09741e52d67d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -149,6 +149,7 @@ public final class HdmiControlManager {
public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;
public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3;
+ /** @removed mistakenly exposed previously */
@IntDef ({
RESULT_SUCCESS,
RESULT_TIMEOUT,
@@ -159,6 +160,7 @@ public final class HdmiControlManager {
RESULT_INCORRECT_MODE,
RESULT_COMMUNICATION_FAILED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ControlCallbackResult {}
/** Control operation is successfully handled by the framework. */
@@ -1135,6 +1137,7 @@ public final class HdmiControlManager {
CEC_SETTING_NAME_QUERY_SAD_MAX,
SETTING_NAME_EARC_ENABLED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SettingName {}
/**
@@ -1157,6 +1160,7 @@ public final class HdmiControlManager {
CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
CEC_SETTING_NAME_QUERY_SAD_MAX,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CecSettingSad {}
// True if we have a logical device of type playback hosted in the system.
diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java
index 8133472961a0..5985c39034ea 100644
--- a/core/java/android/hardware/input/VirtualDpad.java
+++ b/core/java/android/hardware/input/VirtualDpad.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.view.KeyEvent;
import java.util.Arrays;
@@ -52,8 +53,8 @@ public class VirtualDpad extends VirtualInputDevice {
KeyEvent.KEYCODE_DPAD_CENTER)));
/** @hide */
- public VirtualDpad(IVirtualDevice virtualDevice, IBinder token) {
- super(virtualDevice, token);
+ public VirtualDpad(VirtualDpadConfig config, IVirtualDevice virtualDevice, IBinder token) {
+ super(config, virtualDevice, token);
}
/**
@@ -80,7 +81,10 @@ public class VirtualDpad extends VirtualInputDevice {
+ event.getKeyCode()
+ " sent to a VirtualDpad input device.");
}
- mVirtualDevice.sendDpadKeyEvent(mToken, event);
+ if (!mVirtualDevice.sendDpadKeyEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send key event to virtual dpad "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/VirtualInputDevice.java b/core/java/android/hardware/input/VirtualInputDevice.java
index 772ba8e36c5e..affa4ed7e983 100644
--- a/core/java/android/hardware/input/VirtualInputDevice.java
+++ b/core/java/android/hardware/input/VirtualInputDevice.java
@@ -20,6 +20,7 @@ import android.annotation.RequiresPermission;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import java.io.Closeable;
@@ -32,6 +33,8 @@ import java.io.Closeable;
*/
abstract class VirtualInputDevice implements Closeable {
+ protected static final String TAG = "VirtualInputDevice";
+
/**
* The virtual device to which this VirtualInputDevice belongs to.
*/
@@ -42,9 +45,12 @@ abstract class VirtualInputDevice implements Closeable {
*/
protected final IBinder mToken;
+ protected final VirtualInputDeviceConfig mConfig;
+
/** @hide */
- VirtualInputDevice(
+ VirtualInputDevice(VirtualInputDeviceConfig config,
IVirtualDevice virtualDevice, IBinder token) {
+ mConfig = config;
mVirtualDevice = virtualDevice;
mToken = token;
}
@@ -64,10 +70,16 @@ abstract class VirtualInputDevice implements Closeable {
@Override
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void close() {
+ Log.d(TAG, "Closing virtual input device " + mConfig.getInputDeviceName());
try {
mVirtualDevice.unregisterInputDevice(mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public String toString() {
+ return mConfig.toString();
+ }
}
diff --git a/core/java/android/hardware/input/VirtualInputDeviceConfig.java b/core/java/android/hardware/input/VirtualInputDeviceConfig.java
index d3dacc90d1b4..a87980c34f2d 100644
--- a/core/java/android/hardware/input/VirtualInputDeviceConfig.java
+++ b/core/java/android/hardware/input/VirtualInputDeviceConfig.java
@@ -19,6 +19,10 @@ package android.hardware.input;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
+import android.view.Display;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
/**
* Common configurations to create virtual input devices.
@@ -27,6 +31,15 @@ import android.os.Parcel;
*/
@SystemApi
public abstract class VirtualInputDeviceConfig {
+
+ /**
+ * The maximum length of a device name (in bytes in UTF-8 encoding).
+ *
+ * This limitation comes directly from uinput.
+ * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h
+ */
+ private static final int DEVICE_NAME_MAX_LENGTH = 80;
+
/** The vendor id uniquely identifies the company who manufactured the device. */
private final int mVendorId;
/**
@@ -44,18 +57,33 @@ public abstract class VirtualInputDeviceConfig {
mVendorId = builder.mVendorId;
mProductId = builder.mProductId;
mAssociatedDisplayId = builder.mAssociatedDisplayId;
- mInputDeviceName = builder.mInputDeviceName;
+ mInputDeviceName = Objects.requireNonNull(builder.mInputDeviceName);
+
+ if (mAssociatedDisplayId == Display.INVALID_DISPLAY) {
+ throw new IllegalArgumentException(
+ "Display association is required for virtual input devices.");
+ }
+
+ // Comparison is greater or equal because the device name must fit into a const char*
+ // including the \0-terminator. Therefore the actual number of bytes that can be used
+ // for device name is DEVICE_NAME_MAX_LENGTH - 1
+ if (mInputDeviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) {
+ throw new IllegalArgumentException("Input device name exceeds maximum length of "
+ + DEVICE_NAME_MAX_LENGTH + "bytes: " + mInputDeviceName);
+ }
}
protected VirtualInputDeviceConfig(@NonNull Parcel in) {
mVendorId = in.readInt();
mProductId = in.readInt();
mAssociatedDisplayId = in.readInt();
- mInputDeviceName = in.readString8();
+ mInputDeviceName = Objects.requireNonNull(in.readString8());
}
/**
* The vendor id uniquely identifies the company who manufactured the device.
+ *
+ * @see Builder#setVendorId(int) (int)
*/
public int getVendorId() {
return mVendorId;
@@ -64,6 +92,8 @@ public abstract class VirtualInputDeviceConfig {
/**
* The product id uniquely identifies which product within the address space of a given vendor,
* identified by the device's vendor id.
+ *
+ * @see Builder#setProductId(int)
*/
public int getProductId() {
return mProductId;
@@ -71,6 +101,8 @@ public abstract class VirtualInputDeviceConfig {
/**
* The associated display ID of the virtual input device.
+ *
+ * @see Builder#setAssociatedDisplayId(int)
*/
public int getAssociatedDisplayId() {
return mAssociatedDisplayId;
@@ -78,6 +110,8 @@ public abstract class VirtualInputDeviceConfig {
/**
* The name of the virtual input device.
+ *
+ * @see Builder#setInputDeviceName(String)
*/
@NonNull
public String getInputDeviceName() {
@@ -91,6 +125,22 @@ public abstract class VirtualInputDeviceConfig {
dest.writeString8(mInputDeviceName);
}
+ @Override
+ public String toString() {
+ return getClass().getName() + "( "
+ + " name=" + mInputDeviceName
+ + " vendorId=" + mVendorId
+ + " productId=" + mProductId
+ + " associatedDisplayId=" + mAssociatedDisplayId
+ + additionalFieldsToString() + ")";
+ }
+
+ /** @hide */
+ @NonNull
+ String additionalFieldsToString() {
+ return "";
+ }
+
/**
* A builder for {@link VirtualInputDeviceConfig}
*
@@ -101,11 +151,12 @@ public abstract class VirtualInputDeviceConfig {
private int mVendorId;
private int mProductId;
- private int mAssociatedDisplayId;
- @NonNull
+ private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
private String mInputDeviceName;
- /** @see VirtualInputDeviceConfig#getVendorId(). */
+ /**
+ * Sets the vendor id of the device, identifying the company who manufactured the device.
+ */
@NonNull
public T setVendorId(int vendorId) {
mVendorId = vendorId;
@@ -113,24 +164,40 @@ public abstract class VirtualInputDeviceConfig {
}
- /** @see VirtualInputDeviceConfig#getProductId(). */
+ /**
+ * Sets the product id of the device, uniquely identifying the device within the address
+ * space of a given vendor, identified by the device's vendor id.
+ */
@NonNull
public T setProductId(int productId) {
mProductId = productId;
return self();
}
- /** @see VirtualInputDeviceConfig#getAssociatedDisplayId(). */
+ /**
+ * Sets the associated display ID of the virtual input device. Required.
+ *
+ * <p>The input device is restricted to the display with the given ID and may not send
+ * events to any other display.</p>
+ */
@NonNull
public T setAssociatedDisplayId(int displayId) {
mAssociatedDisplayId = displayId;
return self();
}
- /** @see VirtualInputDeviceConfig#getInputDeviceName(). */
+ /**
+ * Sets the name of the virtual input device. Required.
+ *
+ * <p>The name must be unique among all input devices that belong to the same virtual
+ * device.</p>
+ *
+ * <p>The maximum allowed length of the name is 80 bytes in UTF-8 encoding, enforced by
+ * {@code UINPUT_MAX_NAME_SIZE}.</p>
+ */
@NonNull
public T setInputDeviceName(@NonNull String deviceName) {
- mInputDeviceName = deviceName;
+ mInputDeviceName = Objects.requireNonNull(deviceName);
return self();
}
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
index dc47f0820582..c0102bfa4072 100644
--- a/core/java/android/hardware/input/VirtualKeyEvent.java
+++ b/core/java/android/hardware/input/VirtualKeyEvent.java
@@ -172,6 +172,7 @@ public final class VirtualKeyEvent implements Parcelable {
KeyEvent.KEYCODE_BREAK,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_FORWARD,
+ KeyEvent.KEYCODE_LANGUAGE_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SupportedKeycode {
@@ -205,6 +206,14 @@ public final class VirtualKeyEvent implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "VirtualKeyEvent("
+ + " action=" + KeyEvent.actionToString(mAction)
+ + " keyCode=" + KeyEvent.keyCodeToString(mKeyCode)
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
/**
* Returns the key code associated with this event.
*/
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index e569dbf6b6b6..6eb2ae38ed82 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.view.KeyEvent;
/**
@@ -39,8 +40,9 @@ public class VirtualKeyboard extends VirtualInputDevice {
private final int mUnsupportedKeyCode = KeyEvent.KEYCODE_DPAD_CENTER;
/** @hide */
- public VirtualKeyboard(IVirtualDevice virtualDevice, IBinder token) {
- super(virtualDevice, token);
+ public VirtualKeyboard(VirtualKeyboardConfig config,
+ IVirtualDevice virtualDevice, IBinder token) {
+ super(config, virtualDevice, token);
}
/**
@@ -56,7 +58,10 @@ public class VirtualKeyboard extends VirtualInputDevice {
"Unsupported key code " + event.getKeyCode()
+ " sent to a VirtualKeyboard input device.");
}
- mVirtualDevice.sendKeyEvent(mToken, event);
+ if (!mVirtualDevice.sendKeyEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send key event to virtual keyboard "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/VirtualKeyboardConfig.java b/core/java/android/hardware/input/VirtualKeyboardConfig.java
index 6d03065214ca..96a1a36ff361 100644
--- a/core/java/android/hardware/input/VirtualKeyboardConfig.java
+++ b/core/java/android/hardware/input/VirtualKeyboardConfig.java
@@ -99,6 +99,12 @@ public final class VirtualKeyboardConfig extends VirtualInputDeviceConfig implem
dest.writeString8(mLayoutType);
}
+ @Override
+ @NonNull
+ String additionalFieldsToString() {
+ return " languageTag=" + mLanguageTag + " layoutType=" + mLayoutType;
+ }
+
/**
* Builder for creating a {@link VirtualKeyboardConfig}.
*/
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
index 7eba2b8bfdf0..fb0f70049273 100644
--- a/core/java/android/hardware/input/VirtualMouse.java
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -23,6 +23,7 @@ import android.companion.virtual.IVirtualDevice;
import android.graphics.PointF;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.view.MotionEvent;
/**
@@ -38,8 +39,8 @@ import android.view.MotionEvent;
public class VirtualMouse extends VirtualInputDevice {
/** @hide */
- public VirtualMouse(IVirtualDevice virtualDevice, IBinder token) {
- super(virtualDevice, token);
+ public VirtualMouse(VirtualMouseConfig config, IVirtualDevice virtualDevice, IBinder token) {
+ super(config, virtualDevice, token);
}
/**
@@ -52,7 +53,10 @@ public class VirtualMouse extends VirtualInputDevice {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
try {
- mVirtualDevice.sendButtonEvent(mToken, event);
+ if (!mVirtualDevice.sendButtonEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send button event to virtual mouse "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -69,7 +73,10 @@ public class VirtualMouse extends VirtualInputDevice {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
try {
- mVirtualDevice.sendScrollEvent(mToken, event);
+ if (!mVirtualDevice.sendScrollEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send scroll event to virtual mouse "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -85,7 +92,10 @@ public class VirtualMouse extends VirtualInputDevice {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
try {
- mVirtualDevice.sendRelativeEvent(mToken, event);
+ if (!mVirtualDevice.sendRelativeEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send relative event to virtual mouse "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/VirtualMouseButtonEvent.java b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
index dfdd3b45923b..fc42b1581091 100644
--- a/core/java/android/hardware/input/VirtualMouseButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
@@ -110,6 +110,14 @@ public final class VirtualMouseButtonEvent implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "VirtualMouseButtonEvent("
+ + " action=" + MotionEvent.actionToString(mAction)
+ + " button=" + MotionEvent.buttonStateToString(mButtonCode)
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
/**
* Returns the button code associated with this event.
*/
diff --git a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
index e6ad118a2ee0..2a42cfc57c77 100644
--- a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
@@ -61,6 +61,14 @@ public final class VirtualMouseRelativeEvent implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "VirtualMouseRelativeEvent("
+ + " x=" + mRelativeX
+ + " y=" + mRelativeY
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
/**
* Returns the relative x-axis movement, in pixels.
*/
diff --git a/core/java/android/hardware/input/VirtualMouseScrollEvent.java b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
index 4d0a1576b6ee..c89c188a443d 100644
--- a/core/java/android/hardware/input/VirtualMouseScrollEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
@@ -65,6 +65,14 @@ public final class VirtualMouseScrollEvent implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "VirtualMouseScrollEvent("
+ + " x=" + mXAxisMovement
+ + " y=" + mYAxisMovement
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
/**
* Returns the x-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
* indicate scrolling upward; negative values, downward.
diff --git a/core/java/android/hardware/input/VirtualNavigationTouchpad.java b/core/java/android/hardware/input/VirtualNavigationTouchpad.java
index 2854034cd127..3dbb38568f68 100644
--- a/core/java/android/hardware/input/VirtualNavigationTouchpad.java
+++ b/core/java/android/hardware/input/VirtualNavigationTouchpad.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
/**
* A virtual navigation touchpad representing a touch-based input mechanism on a remote device.
@@ -40,8 +41,9 @@ import android.os.RemoteException;
public class VirtualNavigationTouchpad extends VirtualInputDevice {
/** @hide */
- public VirtualNavigationTouchpad(IVirtualDevice virtualDevice, IBinder token) {
- super(virtualDevice, token);
+ public VirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config,
+ IVirtualDevice virtualDevice, IBinder token) {
+ super(config, virtualDevice, token);
}
/**
@@ -52,7 +54,10 @@ public class VirtualNavigationTouchpad extends VirtualInputDevice {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
try {
- mVirtualDevice.sendTouchEvent(mToken, event);
+ if (!mVirtualDevice.sendTouchEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send touch event to virtual navigation touchpad "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/VirtualNavigationTouchpadConfig.java b/core/java/android/hardware/input/VirtualNavigationTouchpadConfig.java
index 8935efa96720..75f7b3e3035e 100644
--- a/core/java/android/hardware/input/VirtualNavigationTouchpadConfig.java
+++ b/core/java/android/hardware/input/VirtualNavigationTouchpadConfig.java
@@ -70,6 +70,12 @@ public final class VirtualNavigationTouchpadConfig extends VirtualInputDeviceCon
dest.writeInt(mWidth);
}
+ @Override
+ @NonNull
+ String additionalFieldsToString() {
+ return " width=" + mWidth + " height=" + mHeight;
+ }
+
@NonNull
public static final Creator<VirtualNavigationTouchpadConfig> CREATOR =
new Creator<VirtualNavigationTouchpadConfig>() {
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.java b/core/java/android/hardware/input/VirtualTouchEvent.java
index 2695a799d610..7936dfef7748 100644
--- a/core/java/android/hardware/input/VirtualTouchEvent.java
+++ b/core/java/android/hardware/input/VirtualTouchEvent.java
@@ -138,6 +138,19 @@ public final class VirtualTouchEvent implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "VirtualTouchEvent("
+ + " pointerId=" + mPointerId
+ + " toolType=" + MotionEvent.toolTypeToString(mToolType)
+ + " action=" + MotionEvent.actionToString(mAction)
+ + " x=" + mX
+ + " y=" + mY
+ + " pressure=" + mPressure
+ + " majorAxisSize=" + mMajorAxisSize
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
/**
* Returns the pointer id associated with this event.
*/
diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java
index 0d07753b9b60..2c800aadef37 100644
--- a/core/java/android/hardware/input/VirtualTouchscreen.java
+++ b/core/java/android/hardware/input/VirtualTouchscreen.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
/**
* A virtual touchscreen representing a touch-based display input mechanism on a remote device.
@@ -34,8 +35,9 @@ import android.os.RemoteException;
@SystemApi
public class VirtualTouchscreen extends VirtualInputDevice {
/** @hide */
- public VirtualTouchscreen(IVirtualDevice virtualDevice, IBinder token) {
- super(virtualDevice, token);
+ public VirtualTouchscreen(VirtualTouchscreenConfig config,
+ IVirtualDevice virtualDevice, IBinder token) {
+ super(config, virtualDevice, token);
}
/**
@@ -46,7 +48,10 @@ public class VirtualTouchscreen extends VirtualInputDevice {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
try {
- mVirtualDevice.sendTouchEvent(mToken, event);
+ if (!mVirtualDevice.sendTouchEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send touch event to virtual touchscreen "
+ + mConfig.getInputDeviceName());
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/VirtualTouchscreenConfig.java b/core/java/android/hardware/input/VirtualTouchscreenConfig.java
index aac341ccb5ed..63084592a2e8 100644
--- a/core/java/android/hardware/input/VirtualTouchscreenConfig.java
+++ b/core/java/android/hardware/input/VirtualTouchscreenConfig.java
@@ -69,6 +69,12 @@ public final class VirtualTouchscreenConfig extends VirtualInputDeviceConfig imp
dest.writeInt(mHeight);
}
+ @Override
+ @NonNull
+ String additionalFieldsToString() {
+ return " width=" + mWidth + " height=" + mHeight;
+ }
+
@NonNull
public static final Creator<VirtualTouchscreenConfig> CREATOR =
new Creator<VirtualTouchscreenConfig>() {
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 01ce7b9c0731..481ec7207c8a 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -15,6 +15,8 @@
*/
package android.hardware.location;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1111,12 +1113,12 @@ public final class ContextHubManager {
}
};
- /** @throws ServiceNotFoundException
- * @hide */
- public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
+ /** @hide */
+ public ContextHubManager(@NonNull IContextHubService service, @NonNull Looper mainLooper) {
+ requireNonNull(service, "service cannot be null");
+ requireNonNull(mainLooper, "mainLooper cannot be null");
+ mService = service;
mMainLooper = mainLooper;
- mService = IContextHubService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
try {
mService.registerCallback(mClientCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index c7ec052b309b..7e5c141a399a 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -109,7 +109,10 @@ public final class ProgramSelector implements Parcelable {
/** @deprecated use {@link ProgramIdentifier} instead */
@Deprecated
public static final int PROGRAM_TYPE_VENDOR_END = 1999;
- /** @deprecated use {@link ProgramIdentifier} instead */
+ /**
+ * @deprecated use {@link ProgramIdentifier} instead
+ * @removed mistakenly exposed previously
+ */
@Deprecated
@IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
PROGRAM_TYPE_INVALID,
@@ -397,6 +400,7 @@ public final class ProgramSelector implements Parcelable {
*/
@Deprecated
public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
+ /** @removed mistakenly exposed previously */
@IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
IDENTIFIER_TYPE_INVALID,
IDENTIFIER_TYPE_AMFM_FREQUENCY,
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 237ec0129ed9..f0f7e8a22e2a 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -123,6 +123,7 @@ public class RadioManager {
/** AM HD radio or DRM band.
* @see BandDescriptor */
public static final int BAND_AM_HD = 3;
+ /** @removed mistakenly exposed previously */
@IntDef(prefix = { "BAND_" }, value = {
BAND_INVALID,
BAND_AM,
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 889d3df0941a..81a023451f16 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -52,6 +52,8 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -738,6 +740,7 @@ public class UsbManager {
FUNCTION_NCM,
FUNCTION_UVC,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbFunctionMode {}
/** @hide */
@@ -748,6 +751,7 @@ public class UsbManager {
GADGET_HAL_V1_2,
GADGET_HAL_V2_0,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbGadgetHalVersion {}
/** @hide */
@@ -759,6 +763,7 @@ public class UsbManager {
USB_HAL_V1_3,
USB_HAL_V2_0,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbHalVersion {}
/**
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index d95924002f1f..4a5c4c8acd25 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -646,12 +646,7 @@ public final class UsbPortStatus implements Parcelable {
* @return array including {@link #COMPLIANCE_WARNING_OTHER},
* {@link #COMPLIANCE_WARNING_DEBUG_ACCESSORY},
* {@link #COMPLIANCE_WARNING_BC_1_2},
- * {@link #COMPLIANCE_WARNING_MISSING_RP},
- * {@link #COMPLIANCE_WARNING_INPUT_POWER_LIMITED},
- * {@link #COMPLIANCE_WARNING_MISSING_DATA_LINES},
- * {@link #COMPLIANCE_WARNING_ENUMERATION_FAIL},
- * {@link #COMPLIANCE_WARNING_FLAKY_CONNECTION},
- * {@link #COMPLIANCE_WARNING_UNRELIABLE_IO}.
+ * {@link #COMPLIANCE_WARNING_MISSING_RP}.
*/
@CheckResult
@NonNull
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 5c5083a691be..63ae28f6ff9d 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -13,3 +13,10 @@ flag {
description: "Flag incompatible charging on COMPLIANCE_WARNING_INPUT_POWER_LIMITED instead of COMPLIANCE_WARNING_OTHER when enabled"
bug: "308700954"
}
+
+flag {
+ name: "enable_report_usb_data_compliance_warning"
+ namespace: "system_sw_usb"
+ description: "Enable reporting USB data compliance warnings from HAL when set"
+ bug: "296119135"
+}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index f16e2439f3f4..e2d215ebfed4 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -33,6 +33,8 @@ import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.window.WindowProviderService;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,8 +74,9 @@ public abstract class AbstractInputMethodService extends WindowProviderService
* {@code null} if {@link #onCreateInputMethodInterface()} is not yet called.
* @hide
*/
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Nullable
- protected final InputMethod getInputMethodInternal() {
+ public final InputMethod getInputMethodInternal() {
return mInputMethod;
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ba80811e198c..18d3e5e02fbe 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -149,6 +149,7 @@ import android.window.OnBackInvokedDispatcher;
import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethod;
@@ -3997,6 +3998,16 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Returns whether the IME navigation bar is currently shown, for testing purposes.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public final boolean isImeNavigationBarShownForTesting() {
+ return mNavigationBarController.isShown();
+ }
+
+ /**
* Used to inject custom {@link InputMethodServiceInternal}.
*
* @return the {@link InputMethodServiceInternal} to be used.
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 8be4c5858694..9c55b0ee0623 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -77,6 +77,10 @@ final class NavigationBarController {
default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
}
+ default boolean isShown() {
+ return false;
+ }
+
default String toDebugString() {
return "No-op implementation";
}
@@ -117,6 +121,13 @@ final class NavigationBarController {
mImpl.onNavButtonFlagsChanged(navButtonFlags);
}
+ /**
+ * Returns whether the IME navigation bar is currently shown.
+ */
+ boolean isShown() {
+ return mImpl.isShown();
+ }
+
String toDebugString() {
return mImpl.toDebugString();
}
@@ -561,6 +572,12 @@ final class NavigationBarController {
}
@Override
+ public boolean isShown() {
+ return mNavigationBarFrame != null
+ && mNavigationBarFrame.getVisibility() == View.VISIBLE;
+ }
+
+ @Override
public String toDebugString() {
return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar
+ " mNavigationBarFrame=" + mNavigationBarFrame
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 0a6be20226b8..eda80c861698 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,14 +85,14 @@ oneway interface INetworkManagementEventObserver {
/**
* Interface data activity status is changed.
*
- * @param transportType The transport type of the data activity change.
+ * @param label label of the data activity change.
* @param active True if the interface is actively transmitting data, false if it is idle.
* @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
* @param uid Uid of this event. It represents the uid that was responsible for waking the
* radio. For those events that are reported by system itself, not from specific uid,
* use -1 for the events which means no uid.
*/
- void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid);
+ void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos, int uid);
/**
* Information about available DNS servers has been received.
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 19ba6a1d22ed..dbb312720373 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.os.IBinder;
import android.os.ServiceManager;
-import com.android.net.flags.Flags;
import com.android.net.module.util.PermissionUtils;
/**
* Constants and utilities for client code communicating with the network stack service.
@@ -104,16 +103,4 @@ public class NetworkStack {
final @NonNull String... otherPermissions) {
PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
}
-
- /**
- * Get setting of the "set_data_saver_via_cm" flag.
- *
- * @hide
- */
- // A workaround for aconfig. Currently, aconfig value read from platform and mainline code can
- // be inconsistent. To avoid the problem, CTS for mainline code can get the flag value by this
- // method.
- public static boolean getDataSaverViaCmFlag() {
- return Flags.setDataSaverViaCm();
- }
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0c95c2ec7a7a..f6beec179d57 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -84,4 +84,6 @@ interface INfcAdapter
boolean isReaderOptionSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
boolean enableReaderOption(boolean enable);
+ boolean isObserveModeSupported();
+ boolean setObserveMode(boolean enabled);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index c7b3b2c03f65..191385a3c13d 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -30,6 +30,7 @@ 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 registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c89759553810..98a980f5e7f8 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1081,6 +1081,61 @@ public final class NfcAdapter {
}
}
+
+ /**
+ * Returns whether the device supports observer mode or not. When observe
+ * mode is enabled, the NFC hardware will listen for NFC readers, but not
+ * respond to them. When observe mode is disabled, the NFC hardware will
+ * resoond to the reader and proceed with the transaction.
+ * @return true if the mode is supported, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean isObserveModeSupported() {
+ try {
+ return sService.isObserveModeSupported();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Disables observe mode to allow the transaction to proceed. See
+ * {@link #isObserveModeSupported()} for a description of observe mode and
+ * use {@link #disallowTransaction()} to enable observe mode and block
+ * transactions again.
+ *
+ * @return boolean indicating success or failure.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean allowTransaction() {
+ try {
+ return sService.setObserveMode(false);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Signals that the transaction has completed and observe mode may be
+ * reenabled. See {@link #isObserveModeSupported()} for a description of
+ * observe mode and use {@link #allowTransaction()} to disable observe
+ * mode and allow transactions to proceed.
+ *
+ * @return boolean indicating success or failure.
+ */
+
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean disallowTransaction() {
+ try {
+ return sService.setObserveMode(true);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
/**
* Resumes default polling for the current device state if polling is paused. Calling
* this while polling is not paused is a no-op.
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 597c948bd515..e331c95288d9 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -21,6 +21,7 @@
package android.nfc.cardemulation;
import android.annotation.FlaggedApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -134,8 +135,9 @@ public final class ApduServiceInfo implements Parcelable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
- List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
@@ -147,7 +149,7 @@ public final class ApduServiceInfo implements Parcelable {
* @hide
*/
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
- List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost,
boolean isEnabled) {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d048b595ad1e..58b6179691e9 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -328,6 +328,24 @@ public final class CardEmulation {
return SELECTION_MODE_ASK_IF_CONFLICT;
}
}
+ /**
+ * Sets whether the system should default to observe mode or not when
+ * the service is in the foreground or the default payment service.
+ *
+ * @param service The component name of the service
+ * @param enable Whether the servic 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) {
+ try {
+ return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ service, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ }
+ return false;
+ }
/**
* Registers a list of AIDs for a specific category for the
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index 55d0e73780a2..7cd2533a7dbf 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -16,11 +16,14 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -29,6 +32,9 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>HostApduService is a convenience {@link Service} class that can be
* extended to emulate an NFC card inside an Android
@@ -230,9 +236,99 @@ public abstract class HostApduService extends Service {
/**
* @hide
*/
+ public static final int MSG_POLLING_LOOP = 4;
+
+ /**
+ * @hide
+ */
public static final String KEY_DATA = "data";
/**
+ * POLLING_LOOP_TYPE_KEY 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 POLLING_LOOP_TYPE_KEY = "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';
+
+ /**
+ * 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 POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+
+ /**
+ * POLLING_LOOP_GAIN_KEY 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 POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * POLLING_LOOP_TIMESTAMP_KEY 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 POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+
+ /**
+ * @hide
+ */
+ public static final String POLLING_LOOP_FRAMES_BUNDLE_KEY =
+ "android.nfc.cardemulation.POLLING_FRAMES";
+
+ /**
* Messenger interface to NfcService for sending responses.
* Only accessed on main thread by the message handler.
*
@@ -255,6 +351,7 @@ public abstract class HostApduService extends Service {
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
+ HostApduService has = HostApduService.this;
byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
@@ -306,6 +403,12 @@ public abstract class HostApduService extends Service {
Log.e(TAG, "RemoteException calling into NfcService.");
}
break;
+ case MSG_POLLING_LOOP:
+ ArrayList<Bundle> frames =
+ msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+ Bundle.class);
+ processPollingFrames(frames);
+ break;
default:
super.handleMessage(msg);
}
@@ -366,6 +469,21 @@ public abstract class HostApduService extends Service {
}
}
+ /**
+ * This method is called when a polling frame has 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
+ * can use this polling frame information to determine how to proceed if it
+ * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The
+ * service must override this method inorder to receive polling frames,
+ * otherwise the base implementation drops the frame.
+ *
+ * @param frame A description of the polling frame.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void processPollingFrames(@NonNull List<Bundle> frame) {
+ }
/**
* <p>This method will be called when a command APDU has been received
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index cd50ace036de..17e042761dbe 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -20,3 +20,31 @@ flag {
description: "Flag for NFC user restriction"
bug: "291187960"
}
+
+flag {
+ name: "nfc_observe_mode"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_observe_mode_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode ST shim"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications ST shim"
+ bug: "294217286"
+}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index a5f8844a2921..cd52b5c0f7f3 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -115,6 +115,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+ static final String XML_ATTR_TIME_IN_FOREGROUND_SERVICE = "time_in_foreground_service";
// We need about 700 bytes per UID
private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 5_000 * 700;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 995622004fa6..0ccc485a34f4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -30,9 +30,11 @@ import com.android.internal.os.BinderCallHeavyHitterWatcher;
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -46,6 +48,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.function.Supplier;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -289,6 +292,33 @@ public class Binder implements IBinder {
sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking);
}
+ private static ThreadLocal<SomeArgs> sIdentity$ravenwood;
+
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
+ private static class IdentitySupplier implements Supplier<SomeArgs> {
+ @Override
+ public SomeArgs get() {
+ final SomeArgs args = SomeArgs.obtain();
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.FALSE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return args;
+ }
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood() {
+ sIdentity$ravenwood = ThreadLocal.withInitial(new IdentitySupplier());
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
*/
@@ -312,8 +342,14 @@ public class Binder implements IBinder {
* Warning: oneway transactions do not receive PID.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingPid();
+ /** @hide */
+ public static final int getCallingPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
+ }
+
/**
* Return the Linux UID assigned to the process that sent you the
* current transaction that is being processed. This UID can be used with
@@ -322,8 +358,14 @@ public class Binder implements IBinder {
* incoming transaction, then its own UID is returned.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingUid();
+ /** @hide */
+ public static final int getCallingUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
+ }
+
/**
* Returns {@code true} if the current thread is currently executing an
* incoming transaction.
@@ -331,6 +373,7 @@ public class Binder implements IBinder {
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native boolean isDirectlyHandlingTransactionNative();
/** @hide */
@@ -344,6 +387,7 @@ public class Binder implements IBinder {
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isDirectlyHandlingTransaction() {
return sIsHandlingBinderTransaction || isDirectlyHandlingTransactionNative();
}
@@ -363,8 +407,15 @@ public class Binder implements IBinder {
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native boolean hasExplicitIdentity();
+ /** @hide */
+ private static boolean hasExplicitIdentity$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().arg1
+ == Boolean.TRUE;
+ }
+
/**
* Return the Linux UID assigned to the process that sent the transaction
* currently being processed.
@@ -373,6 +424,7 @@ public class Binder implements IBinder {
* executing an incoming transaction and the calling identity has not been
* explicitly set with {@link #clearCallingIdentity()}
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int getCallingUidOrThrow() {
if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
throw new IllegalStateException(
@@ -434,8 +486,26 @@ public class Binder implements IBinder {
* @see #restoreCallingIdentity(long)
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native long clearCallingIdentity();
+ /** @hide */
+ public static final long clearCallingIdentity$ravenwood() {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ long res = ((long) args.argi1 << 32) | args.argi2;
+ if (args.arg1 == Boolean.TRUE) {
+ res |= (0x1 << 30);
+ } else {
+ res &= ~(0x1 << 30);
+ }
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.TRUE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return res;
+ }
+
/**
* Restore the identity of the incoming IPC on the current thread
* back to a previously identity that was returned by {@link
@@ -447,8 +517,18 @@ public class Binder implements IBinder {
* @see #clearCallingIdentity
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void restoreCallingIdentity(long token);
+ /** @hide */
+ public static final void restoreCallingIdentity$ravenwood(long token) {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ args.arg1 = ((token & (0x1 << 30)) != 0) ? Boolean.TRUE : Boolean.FALSE;
+ args.argi1 = (int) (token >> 32);
+ args.argi2 = (int) (token & ~(0x1 << 30));
+ }
+
/**
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
@@ -644,8 +724,14 @@ public class Binder implements IBinder {
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void flushPendingCommands();
+ /** @hide */
+ public static final void flushPendingCommands$ravenwood() {
+ // Ravenwood doesn't support IPC; ignored
+ }
+
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -703,6 +789,7 @@ public class Binder implements IBinder {
* <p>If you're creating a Binder token (a Binder object without an attached interface),
* you should use {@link #Binder(String)} instead.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder() {
this(null);
}
@@ -719,6 +806,7 @@ public class Binder implements IBinder {
* Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
* help identify them.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
if (mObject != 0L) {
@@ -742,6 +830,7 @@ public class Binder implements IBinder {
* will be implemented for you to return the given owner IInterface when
* the corresponding descriptor is requested.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
@@ -750,6 +839,7 @@ public class Binder implements IBinder {
/**
* Default implementation returns an empty interface name.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getInterfaceDescriptor() {
return mDescriptor;
}
@@ -758,6 +848,7 @@ public class Binder implements IBinder {
* Default implementation always returns true -- if you got here,
* the object is alive.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean pingBinder() {
return true;
}
@@ -768,6 +859,7 @@ public class Binder implements IBinder {
* Note that if you're calling on a local binder, this always returns true
* because your process is alive if you're calling it.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isBinderAlive() {
return true;
}
@@ -777,6 +869,7 @@ public class Binder implements IBinder {
* to return the associated {@link IInterface} if it matches the requested
* descriptor.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
@@ -1250,12 +1343,14 @@ public class Binder implements IBinder {
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}
@@ -1283,6 +1378,7 @@ public class Binder implements IBinder {
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long getNativeBBinderHolder();
private static long getNativeBBinderHolder$ravenwood() {
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index e06b0f9f4bc2..61aa222ef482 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -19,7 +19,29 @@ import android.os.RemoteException;
/**
* The object you are calling has died, because its hosting process
- * no longer exists.
+ * no longer exists, or there has been a low-level binder error.
+ *
+ * If you get this exception from a system service, the error is
+ * usually nonrecoverable as the framework will restart. If you
+ * receive this error from an app, at a minimum, you should
+ * recover by resetting the connection. For instance, you should
+ * drop the binder, clean up associated state, and reset your
+ * connection to the service which through this error. In order
+ * to simplify your error recovery paths, you may also want to
+ * "simply" restart your process. However, this may not be an
+ * option if the service you are talking to is unreliable or
+ * crashes frequently.
+ *
+ * If this isn't from a service death and is instead from a
+ * low-level binder error, it will be from:
+ * - a oneway call queue filling up (too many oneway calls)
+ * - from the binder buffer being filled up, so that the transaction
+ * is rejected.
+ *
+ * In these cases, more information about the error will be
+ * logged. However, there isn't a good way to differentiate
+ * this information at runtime. So, you should handle the
+ * error, as if the service died.
*/
public class DeadObjectException extends RemoteException {
public DeadObjectException() {
diff --git a/core/java/android/os/DeadSystemRuntimeException.java b/core/java/android/os/DeadSystemRuntimeException.java
index 1e869249eb9d..3b107984cebf 100644
--- a/core/java/android/os/DeadSystemRuntimeException.java
+++ b/core/java/android/os/DeadSystemRuntimeException.java
@@ -18,9 +18,12 @@ package android.os;
/**
* Exception thrown when a call into system_server resulted in a
- * DeadObjectException, meaning that the system_server has died. There's
- * nothing apps can do at this point - the system will automatically restart -
- * so there's no point in catching this.
+ * DeadObjectException, meaning that the system_server has died or
+ * experienced a low-level binder error. There's nothing apps can
+ * do at this point - the system will automatically restart - so
+ * there's no point in catching this.
+ *
+ * See {@link android.os.DeadObjectException}.
*
* @hide
*/
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 90e4b17250d8..91c2965c2505 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -194,6 +194,7 @@ public interface IBinder {
* Limit that should be placed on IPC sizes, in bytes, to keep them safely under the transaction
* buffer limit.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
static int getSuggestedMaxIpcSizeBytes() {
return MAX_IPC_SIZE;
}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index c6c8adc4d8a9..bcffa45fbbd2 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -111,4 +111,9 @@ interface IThermalService {
* occur; returns NaN if the headroom or forecast is unavailable
*/
float getThermalHeadroom(int forecastSeconds);
+
+ /**
+ * @return thermal headroom for each thermal status
+ */
+ float[] getThermalHeadroomThresholds();
}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index f30dd20d7087..0f2756914fa6 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -33,13 +33,13 @@ interface IVibratorManagerService {
boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibration vibration, in VibrationAttributes attributes);
- void vibrate(int uid, int displayId, String opPkg, in CombinedVibration vibration,
+ void vibrate(int uid, int deviceId, String opPkg, in CombinedVibration vibration,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(int usageFilter, IBinder token);
// Async oneway APIs.
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
- oneway void performHapticFeedback(int uid, int displayId, String opPkg, int constant,
+ oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
boolean always, String reason, IBinder token);
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 8f7725ecaba0..655debc84d1d 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -87,3 +87,7 @@ per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com
# PerformanceHintManager
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
+
+# IThermal interfaces
+per-file IThermal* = file:/THERMAL_OWNERS
+
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d2c17556bb2f..11bddfb5b0f0 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -42,14 +43,17 @@ import android.view.Display;
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.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -1180,6 +1184,8 @@ public final class PowerManager {
private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
mListenerMap = new ArrayMap<>();
+ private final Object mThermalHeadroomThresholdsLock = new Object();
+ private float[] mThermalHeadroomThresholds = null;
/**
* {@hide}
@@ -2636,6 +2642,7 @@ public final class PowerManager {
public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN;
/** @hide */
+ @Target(ElementType.TYPE_USE)
@IntDef(prefix = { "THERMAL_STATUS_" }, value = {
THERMAL_STATUS_NONE,
THERMAL_STATUS_LIGHT,
@@ -2800,6 +2807,63 @@ public final class PowerManager {
}
/**
+ * Gets the thermal headroom thresholds for all available thermal throttling status above
+ * {@link #THERMAL_STATUS_NONE}.
+ * <p>
+ * A thermal status key in the returned map is only set if the device manufacturer has the
+ * corresponding threshold defined for at least one of its sensors. If it's set, one should
+ * expect to see that from {@link #getCurrentThermalStatus()} or
+ * {@link OnThermalStatusChangedListener#onThermalStatusChanged(int)}.
+ * <p>
+ * The headroom threshold is used to interpret the possible thermal throttling status based on
+ * the headroom prediction. For example, if the headroom threshold for
+ * {@link #THERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
+ * (or {@code getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system could
+ * be in lightly throttled state if the workload remains the same. The app can consider
+ * taking actions according to the nearest throttling status the difference between the headroom
+ * and the threshold.
+ * <p>
+ * For new devices it's guaranteed to have a single sensor, but for older devices with multiple
+ * sensors reporting different threshold values, the minimum threshold is taken to be
+ * conservative on predictions. Thus, when reading real-time headroom, it's not guaranteed that
+ * a real-time value of 0.75 (or {@code getThermalHeadroom(0)}=0.75) exceeding the threshold of
+ * 0.7 above will always come with lightly throttled state
+ * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_LIGHT}) but it can be lower
+ * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_NONE}). While it's always guaranteed that
+ * the device won't be throttled heavier than the unmet threshold's state, so a real-time
+ * headroom of 0.75 will never come with {@link #THERMAL_STATUS_MODERATE} but lower, and 0.65
+ * will never come with {@link #THERMAL_STATUS_LIGHT} but {@link #THERMAL_STATUS_NONE}.
+ * <p>
+ * The returned map of thresholds will not change between calls to this function, so it's
+ * best to call this once on initialization. Modifying the result will not change the thresholds
+ * cached by the system, and a new call to the API will get a new copy.
+ *
+ * @return map from each thermal status to its thermal headroom
+ * @throws IllegalStateException if the thermal service is not ready
+ * @throws UnsupportedOperationException if the feature is not enabled
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS)
+ public @NonNull Map<@ThermalStatus Integer, Float> getThermalHeadroomThresholds() {
+ try {
+ synchronized (mThermalHeadroomThresholdsLock) {
+ if (mThermalHeadroomThresholds == null) {
+ mThermalHeadroomThresholds = mThermalService.getThermalHeadroomThresholds();
+ }
+ final ArrayMap<Integer, Float> ret = new ArrayMap<>(THERMAL_STATUS_SHUTDOWN);
+ for (int status = THERMAL_STATUS_LIGHT; status <= THERMAL_STATUS_SHUTDOWN;
+ status++) {
+ if (!Float.isNaN(mThermalHeadroomThresholds[status])) {
+ ret.put(status, mThermalHeadroomThresholds[status]);
+ }
+ }
+ return ret;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* If true, the doze component is not started until after the screen has been
* turned off and the screen off animation has been performed.
* @hide
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 677143afd4fb..13572fb1bbbb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,6 +34,9 @@ import android.system.StructPollfd;
import android.util.Pair;
import android.webkit.WebViewZygote;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
+
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -829,18 +832,47 @@ public class Process {
/**
* Returns true if the current process is a 64-bit runtime.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final boolean is64Bit() {
return VMRuntime.getRuntime().is64Bit();
}
+ /** @hide */
+ public static final boolean is64Bit$ravenwood() {
+ return "amd64".equals(System.getProperty("os.arch"));
+ }
+
+ private static SomeArgs sIdentity$ravenwood;
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood(int uid, int pid) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = uid;
+ args.argi2 = pid;
+ sIdentity$ravenwood = args;
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Returns the identifier of this process, which can be used with
* {@link #killProcess} and {@link #sendSignal}.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myPid() {
return Os.getpid();
}
+ /** @hide */
+ public static final int myPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi2;
+ }
+
/**
* Returns the identifier of this process' parent.
* @hide
@@ -864,16 +896,23 @@ public class Process {
* app-specific sandbox. It is different from {@link #myUserHandle} in that
* a uid identifies a specific app sandbox in a specific user.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myUid() {
return Os.getuid();
}
+ /** @hide */
+ public static final int myUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi1;
+ }
+
/**
* Returns this process's user handle. This is the
* user the process is running under. It is distinct from
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static UserHandle myUserHandle() {
return UserHandle.of(UserHandle.getUserId(myUid()));
}
@@ -882,6 +921,7 @@ public class Process {
* Returns whether the given uid belongs to a system core component or not.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isCoreUid(int uid) {
return UserHandle.isCore(uid);
}
@@ -892,6 +932,7 @@ public class Process {
* @return Whether the uid corresponds to an application sandbox running in
* a specific user.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isApplicationUid(int uid) {
return UserHandle.isApp(uid);
}
@@ -899,6 +940,7 @@ public class Process {
/**
* Returns whether the current process is in an isolated sandbox.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolated() {
return isIsolated(myUid());
}
@@ -910,6 +952,7 @@ public class Process {
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
publicAlternatives = "Use {@link #isIsolatedUid(int)} instead.")
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolated(int uid) {
return isIsolatedUid(uid);
}
@@ -917,6 +960,7 @@ public class Process {
/**
* Returns whether the process with the given {@code uid} is an isolated sandbox.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolatedUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
@@ -930,6 +974,7 @@ public class Process {
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isSdkSandboxUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID);
@@ -943,6 +988,7 @@ public class Process {
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int getAppUidForSdkSandboxUid(int uid) {
return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
@@ -955,6 +1001,7 @@ public class Process {
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int toSdkSandboxUid(int uid) {
return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
@@ -962,6 +1009,7 @@ public class Process {
/**
* Returns whether the current process is a sdk sandbox process.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isSdkSandbox() {
return isSdkSandboxUid(myUid());
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index d4688f8794a4..f71c2695c677 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -57,6 +57,8 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.SignatureException;
@@ -166,6 +168,7 @@ public class RecoverySystem {
RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ResumeOnRebootRebootErrorCode {}
/**
diff --git a/core/java/android/os/RevocableFileDescriptor.java b/core/java/android/os/RevocableFileDescriptor.java
index ac2cd60c81b2..7093c49fb4da 100644
--- a/core/java/android/os/RevocableFileDescriptor.java
+++ b/core/java/android/os/RevocableFileDescriptor.java
@@ -73,11 +73,26 @@ public class RevocableFileDescriptor {
init(context, fd);
}
+ public RevocableFileDescriptor(Context context, FileDescriptor fd, Handler handler)
+ throws IOException {
+ init(context, fd, handler);
+ }
+
/** {@hide} */
public void init(Context context, FileDescriptor fd) throws IOException {
+ init(context, fd, null);
+ }
+
+ /** {@hide} */
+ public void init(Context context, FileDescriptor fd, Handler handler) throws IOException {
mInner = fd;
- mOuter = context.getSystemService(StorageManager.class)
- .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ StorageManager sm = context.getSystemService(StorageManager.class);
+ if (handler != null) {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback,
+ handler);
+ } else {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ }
}
/**
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 831ca86504af..49a0bd3289aa 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -24,6 +24,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.location.ILocationManager;
import android.location.LocationTime;
+import android.text.format.DateUtils;
import android.util.Slog;
import dalvik.annotation.optimization.CriticalNative;
@@ -125,6 +126,7 @@ public final class SystemClock {
*
* @param ms to sleep before returning, in milliseconds of uptime.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void sleep(long ms)
{
long start = uptimeMillis();
@@ -186,8 +188,16 @@ public final class SystemClock {
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long uptimeMillis();
+ /** @hide */
+ public static long uptimeMillis$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000);
+ }
+
/**
* Returns nanoseconds since boot, not counting time spent in deep sleep.
*
@@ -195,8 +205,16 @@ public final class SystemClock {
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long uptimeNanos();
+ /** @hide */
+ public static long uptimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000_000_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, not counting time spent
* in deep sleep.
@@ -218,8 +236,15 @@ public final class SystemClock {
* @return elapsed milliseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long elapsedRealtime();
+ /** @hide */
+ public static long elapsedRealtime$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, including time spent in
* sleep.
@@ -241,8 +266,15 @@ public final class SystemClock {
* @return elapsed nanoseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long elapsedRealtimeNanos();
+ /** @hide */
+ public static long elapsedRealtimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000);
+ }
+
/**
* Returns milliseconds running in the current thread.
*
@@ -271,8 +303,15 @@ public final class SystemClock {
*/
@UnsupportedAppUsage
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long currentTimeMicro();
+ /** @hide */
+ public static long currentTimeMicro$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() / 1000L;
+ }
+
/**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index ee90834c15ef..bc85412e851b 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -137,8 +137,8 @@ public class SystemVibratorManager extends VibratorManager {
return;
}
try {
- mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
- reason, mToken);
+ mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -152,8 +152,8 @@ public class SystemVibratorManager extends VibratorManager {
}
try {
mService.performHapticFeedback(
- Process.myUid(), mContext.getAssociatedDisplayId(), mPackageName, constant,
- always, reason, mToken);
+ Process.myUid(), mContext.getDeviceId(), mPackageName, constant, always, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 2d6e09a1b268..b5029a6aaff3 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -153,5 +153,11 @@
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests"
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsOsTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 03a1b6f7fe01..3eea94eaf2e6 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -71,7 +71,8 @@ public final class UidBatteryConsumer extends BatteryConsumer {
static final int COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN = COLUMN_INDEX_UID + 1;
static final int COLUMN_INDEX_TIME_IN_FOREGROUND = COLUMN_INDEX_UID + 2;
static final int COLUMN_INDEX_TIME_IN_BACKGROUND = COLUMN_INDEX_UID + 3;
- static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 4;
+ static final int COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE = COLUMN_INDEX_UID + 4;
+ static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 5;
UidBatteryConsumer(BatteryConsumerData data) {
super(data);
@@ -92,17 +93,35 @@ public final class UidBatteryConsumer extends BatteryConsumer {
/**
* Returns the amount of time in milliseconds this UID spent in the specified state.
+ * @deprecated use {@link #getTimeInProcessStateMs} instead.
*/
+ @Deprecated
public long getTimeInStateMs(@State int state) {
switch (state) {
case STATE_BACKGROUND:
- return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND);
+ return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND)
+ + mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE);
case STATE_FOREGROUND:
return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND);
}
return 0;
}
+ /**
+ * Returns the amount of time in milliseconds this UID spent in the specified process state.
+ */
+ public long getTimeInProcessStateMs(@ProcessState int state) {
+ switch (state) {
+ case PROCESS_STATE_BACKGROUND:
+ return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND);
+ case PROCESS_STATE_FOREGROUND:
+ return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND);
+ case PROCESS_STATE_FOREGROUND_SERVICE:
+ return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE);
+ }
+ return 0;
+ }
+
@Override
public void dump(PrintWriter pw, boolean skipEmptyComponents) {
pw.print("UID ");
@@ -158,9 +177,11 @@ public final class UidBatteryConsumer extends BatteryConsumer {
packageWithHighestDrain);
}
serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
- getTimeInStateMs(STATE_FOREGROUND));
+ getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND));
serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
- getTimeInStateMs(STATE_BACKGROUND));
+ getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND));
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE,
+ getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE));
mPowerComponents.writeToXml(serializer);
serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
}
@@ -180,10 +201,13 @@ public final class UidBatteryConsumer extends BatteryConsumer {
consumerBuilder.setPackageWithHighestDrain(
parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
- consumerBuilder.setTimeInStateMs(STATE_FOREGROUND,
+ consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND,
parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
- consumerBuilder.setTimeInStateMs(STATE_BACKGROUND,
+ consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND,
parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
+ consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
+ parser.getAttributeLong(null,
+ BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE));
while (!(eventType == XmlPullParser.END_TAG
&& parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
&& eventType != XmlPullParser.END_DOCUMENT) {
@@ -255,7 +279,9 @@ public final class UidBatteryConsumer extends BatteryConsumer {
/**
* Sets the duration, in milliseconds, that this UID was active in a particular state,
* such as foreground or background.
+ * @deprecated use {@link #setTimeInProcessStateMs} instead.
*/
+ @Deprecated
@NonNull
public Builder setTimeInStateMs(@State int state, long timeInStateMs) {
switch (state) {
@@ -272,6 +298,28 @@ public final class UidBatteryConsumer extends BatteryConsumer {
}
/**
+ * Sets the duration, in milliseconds, that this UID was active in a particular process
+ * state, such as foreground service.
+ */
+ @NonNull
+ public Builder setTimeInProcessStateMs(@ProcessState int state, long timeInProcessStateMs) {
+ switch (state) {
+ case PROCESS_STATE_FOREGROUND:
+ mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInProcessStateMs);
+ break;
+ case PROCESS_STATE_BACKGROUND:
+ mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInProcessStateMs);
+ break;
+ case PROCESS_STATE_FOREGROUND_SERVICE:
+ mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE, timeInProcessStateMs);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported process state: " + state);
+ }
+ return this;
+ }
+
+ /**
* Marks the UidBatteryConsumer for exclusion from the result set.
*/
public Builder excludeFromBatteryUsageStats() {
@@ -285,12 +333,15 @@ public final class UidBatteryConsumer extends BatteryConsumer {
public Builder add(UidBatteryConsumer consumer) {
mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents);
- setTimeInStateMs(STATE_FOREGROUND,
+ setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND,
mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND)
- + consumer.getTimeInStateMs(STATE_FOREGROUND));
- setTimeInStateMs(STATE_BACKGROUND,
+ + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND));
+ setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND,
mData.getLong(COLUMN_INDEX_TIME_IN_BACKGROUND)
- + consumer.getTimeInStateMs(STATE_BACKGROUND));
+ + consumer.getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND));
+ setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
+ mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE)
+ + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE));
if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
mPackageWithHighestDrain = consumer.getPackageWithHighestDrain();
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index b7e3068a437c..0a8f62fd56d8 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -25,6 +25,9 @@ import android.os.IUpdateEngine;
import android.os.IUpdateEngineCallback;
import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* UpdateEngine handles calls to the update engine which takes care of A/B OTA
* updates. It wraps up the update engine Binder APIs and exposes them as
@@ -178,6 +181,7 @@ public class UpdateEngine {
ErrorCodeConstants.NOT_ENOUGH_SPACE,
ErrorCodeConstants.DEVICE_CORRUPTED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
/**
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index cac7f3b74185..0644ef1c788f 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -36,6 +36,7 @@ import java.util.Random;
/**
* Representation of a user on the device.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class UserHandle implements Parcelable {
// NOTE: keep logic in sync with system/core/libcutils/multiuser.c
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 57962f661464..08d6e028f08c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -204,6 +204,8 @@ public class UserManager {
* the user in locked state so that a direct boot aware DPC could reset the password.
* Should not be used together with
* {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED} or an exception will be thrown.
+ * This flag is currently only allowed for {@link #isManagedProfile() managed profiles};
+ * usage on other profiles may result in an Exception.
* @hide
*/
public static final int QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL = 0x2;
@@ -246,7 +248,7 @@ public class UserManager {
@SystemApi
public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 0x4;
- /** @hide */
+ /** @removed mistakenly exposed as system-api previously */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "RESTRICTION_" }, value = {
RESTRICTION_NOT_SET,
@@ -254,7 +256,6 @@ public class UserManager {
RESTRICTION_SOURCE_DEVICE_OWNER,
RESTRICTION_SOURCE_PROFILE_OWNER
})
- @SystemApi
public @interface UserRestrictionSource {}
/**
@@ -5080,6 +5081,32 @@ public class UserManager {
}
/**
+ * Returns list of the profiles of the given user, including userId itself, as well as the
+ * communal profile, if there is one.
+ *
+ * <p>Note that this returns both enabled and not enabled profiles.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
+ *
+ * @hide
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
+ public List<UserInfo> getProfilesIncludingCommunal(@UserIdInt int userId) {
+ final List<UserInfo> profiles = getProfiles(userId);
+ final UserHandle communalProfile = getCommunalProfile();
+ if (communalProfile != null) {
+ final UserInfo communalInfo = getUserInfo(communalProfile.getIdentifier());
+ if (communalInfo != null) {
+ profiles.add(communalInfo);
+ }
+ }
+ return profiles;
+ }
+
+ /**
* Checks if the 2 provided user handles belong to the same profile group.
*
* @param user one of the two user handles to check.
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 940ddf2b2597..a78f221fc962 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -29,6 +29,13 @@ flag {
}
flag {
+ name: "allow_thermal_headroom_thresholds"
+ namespace: "game"
+ description: "Enable thermal headroom thresholds API"
+ bug: "288119641"
+}
+
+flag {
name: "allow_private_profile"
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2d1802ae85e5..6853892348d9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2133,6 +2133,7 @@ public class StorageManager {
MOUNT_MODE_EXTERNAL_PASS_THROUGH,
MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE
})
+ @Retention(RetentionPolicy.SOURCE)
/** @hide */
public @interface MountMode {}
diff --git a/core/java/android/permission/IOnPermissionsChangeListener.aidl b/core/java/android/permission/IOnPermissionsChangeListener.aidl
index cc52a7210737..afacf1a74ca6 100644
--- a/core/java/android/permission/IOnPermissionsChangeListener.aidl
+++ b/core/java/android/permission/IOnPermissionsChangeListener.aidl
@@ -21,5 +21,5 @@ package android.permission;
* {@hide}
*/
oneway interface IOnPermissionsChangeListener {
- void onPermissionsChanged(int uid);
+ void onPermissionsChanged(int uid, String deviceId);
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e10ea10e2a29..7a158c548a38 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1725,7 +1725,7 @@ public final class PermissionManager {
}
private final class OnPermissionsChangeListenerDelegate
- extends IOnPermissionsChangeListener.Stub implements Handler.Callback{
+ extends IOnPermissionsChangeListener.Stub implements Handler.Callback {
private static final int MSG_PERMISSIONS_CHANGED = 1;
private final PackageManager.OnPermissionsChangedListener mListener;
@@ -1738,8 +1738,8 @@ public final class PermissionManager {
}
@Override
- public void onPermissionsChanged(int uid) {
- mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
+ public void onPermissionsChanged(int uid, String deviceId) {
+ mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget();
}
@Override
@@ -1747,7 +1747,8 @@ public final class PermissionManager {
switch (msg.what) {
case MSG_PERMISSIONS_CHANGED: {
final int uid = msg.arg1;
- mListener.onPermissionsChanged(uid);
+ final String deviceId = msg.obj.toString();
+ mListener.onPermissionsChanged(uid, deviceId);
return true;
}
default:
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 7369740bc43c..dc86e3f57c40 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -17,6 +17,7 @@ flag {
flag {
name: "role_controller_in_system_server"
+ is_fixed_read_only: true
namespace: "permissions"
description: "enable role controller in system server"
bug: "302562590"
@@ -44,6 +45,13 @@ flag {
}
flag {
+ name: "enhanced_confirmation_mode_apis"
+ namespace: "permissions"
+ description: "enable enhanced confirmation mode apis"
+ bug: "310220212"
+}
+
+flag {
name: "op_enable_mobile_data_by_user"
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c012ff34bfab..33c15d775ff1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12817,15 +12817,6 @@ public final class Settings {
"render_shadows_in_compositor";
/**
- * If true, submit buffers using blast in ViewRootImpl.
- * (0 = false, 1 = true)
- * @hide
- */
- @Readable
- public static final String DEVELOPMENT_USE_BLAST_ADAPTER_VR =
- "use_blast_adapter_vr";
-
- /**
* Path to the WindowManager display settings file. If unset, the default file path will
* be used.
*
@@ -19039,6 +19030,14 @@ public final class Settings {
public static final int BATTERY_SAVER_MODE_CUSTOM = 4;
/**
+ Whether 1P apps vote for enabling data during different modes,
+ i.e. BTM, BBSM
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final String CONNECTIVITY_KEEP_DATA_ON = "wear_connectivity_keep_data_on";
+
+ /**
* The maximum ambient mode duration when an activity is allowed to auto resume.
* @hide
*/
@@ -19464,6 +19463,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String WEAR_MEDIA_CONTROLS_PACKAGE = "wear_media_controls_package";
/**
@@ -19471,6 +19471,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String WEAR_MEDIA_SESSIONS_PACKAGE = "wear_media_sessions_package";
/*
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index bcda25a1bf3b..72c436ee6d41 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -17,6 +17,7 @@
package android.provider;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -49,6 +50,7 @@ import android.text.TextUtils;
import android.util.Patterns;
import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -3191,8 +3193,8 @@ public final class Telephony {
* Sets whether the PDU session brought up by this APN should always be on.
* See 3GPP TS 23.501 section 5.6.13
* <P>Type: INTEGER</P>
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String ALWAYS_ON = "always_on";
/**
@@ -3302,18 +3304,16 @@ public final class Telephony {
* The MTU (maximum transmit unit) size of the mobile interface for IPv4 to which the APN is
* connected, in bytes.
* <p>Type: INTEGER </p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String MTU_V4 = "mtu_v4";
/**
* The MTU (maximum transmit unit) size of the mobile interface for IPv6 to which the APN is
* connected, in bytes.
* <p>Type: INTEGER </p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String MTU_V6 = "mtu_v6";
/**
@@ -3335,17 +3335,15 @@ public final class Telephony {
/**
* {@code true} if this APN visible to the user, {@code false} otherwise.
* <p>Type: INTEGER (boolean)</p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String USER_VISIBLE = "user_visible";
/**
* {@code true} if the user allowed to edit this APN, {@code false} otherwise.
* <p>Type: INTEGER (boolean)</p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String USER_EDITABLE = "user_editable";
/**
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 9bdd0c2db779..0133bd822182 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -36,10 +36,3 @@ flag {
description: "Collect sepolicy hash from sysfs"
bug: "308471499"
}
-
-flag {
- name: "extend_ecm_to_all_settings"
- namespace: "responsible_apis"
- description: "Allow all app settings to be restrictable via configuration"
- bug: "297372999"
-}
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
new file mode 100644
index 000000000000..4e5588cce1c9
--- /dev/null
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -0,0 +1,22 @@
+package: "android.security"
+
+flag {
+ name: "extend_ecm_to_all_settings"
+ namespace: "responsible_apis"
+ description: "Allow all app settings to be restrictable via configuration"
+ bug: "297372999"
+}
+
+flag {
+ name: "asm_restrictions_enabled"
+ namespace: "responsible_apis"
+ description: "Enables ASM restrictions for activity starts and finishes"
+ bug: "230590090"
+}
+
+flag {
+ name: "asm_toasts_enabled"
+ namespace: "responsible_apis"
+ description: "Enables toasts when ASM restrictions are triggered"
+ bug: "230590090"
+}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 4d33bfd1e94a..d76fa5be4940 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -16,8 +16,11 @@
package android.service.notification;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Flags;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
@@ -57,7 +60,6 @@ public final class Condition implements Parcelable {
* Indicates that Do Not Disturb should be turned on.
*/
public static final int STATE_TRUE = 1;
-
public static final int STATE_UNKNOWN = 2;
public static final int STATE_ERROR = 3;
@@ -90,6 +92,33 @@ public final class Condition implements Parcelable {
public final int flags;
public final int icon;
+ /** @hide */
+ @IntDef(prefix = { "SOURCE_" }, value = {
+ SOURCE_UNKNOWN,
+ SOURCE_USER_ACTION,
+ SOURCE_SCHEDULE,
+ SOURCE_CONTEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
+ /** The state is changing due to an unknown reason. */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int SOURCE_UNKNOWN = 0;
+ /** The state is changing due to an explicit user action. */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int SOURCE_USER_ACTION = 1;
+ /** The state is changing due to an automatic schedule (alarm, set time, etc). */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int SOURCE_SCHEDULE = 2;
+ /** The state is changing due to a change in context (such as detected driving or sleeping). */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int SOURCE_CONTEXT = 3;
+
+ /** The source of, or reason for, the state change represented by this Condition. **/
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public final @Source int source;
+
/**
* The maximum string length for any string contained in this condition.
* @hide
@@ -99,14 +128,48 @@ public final class Condition implements Parcelable {
/**
* An object representing the current state of a {@link android.app.AutomaticZenRule}.
* @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
- * @param summary a user visible description of the rule state.
+ * @param summary a user visible description of the rule state
+ * @param state whether the mode should be activated or deactivated
*/
+ // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source.
public Condition(Uri id, String summary, int state) {
- this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
+ this(id, summary, "", "", -1, state, SOURCE_UNKNOWN, FLAG_RELEVANT_ALWAYS);
+ }
+
+ /**
+ * An object representing the current state of a {@link android.app.AutomaticZenRule}.
+ * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
+ * @param summary a user visible description of the rule state
+ * @param state whether the mode should be activated or deactivated
+ * @param source the source of, or reason for, the state change represented by this Condition
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public Condition(@Nullable Uri id, @Nullable String summary, @State int state,
+ @Source int source) {
+ this(id, summary, "", "", -1, state, source, FLAG_RELEVANT_ALWAYS);
}
+ // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source.
public Condition(Uri id, String summary, String line1, String line2, int icon,
int state, int flags) {
+ this(id, summary, line1, line2, icon, state, SOURCE_UNKNOWN, flags);
+ }
+
+ /**
+ * An object representing the current state of a {@link android.app.AutomaticZenRule}.
+ * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
+ * @param summary a user visible description of the rule state
+ * @param line1 a user-visible description of when the rule will end
+ * @param line2 a continuation of the user-visible description of when the rule will end
+ * @param icon an icon representing this condition
+ * @param state whether the mode should be activated or deactivated
+ * @param source the source of, or reason for, the state change represented by this Condition
+ * @param flags flags on this condition
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public Condition(@Nullable Uri id, @Nullable String summary, @Nullable String line1,
+ @Nullable String line2, int icon, @State int state, @Source int source,
+ int flags) {
if (id == null) throw new IllegalArgumentException("id is required");
if (summary == null) throw new IllegalArgumentException("summary is required");
if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
@@ -116,6 +179,7 @@ public final class Condition implements Parcelable {
this.line2 = getTrimmedString(line2);
this.icon = icon;
this.state = state;
+ this.source = source;
this.flags = flags;
}
@@ -129,6 +193,7 @@ public final class Condition implements Parcelable {
source.readString(),
source.readInt(),
source.readInt(),
+ Flags.modesApi() ? source.readInt() : SOURCE_UNKNOWN,
source.readInt());
}
@@ -144,20 +209,27 @@ public final class Condition implements Parcelable {
dest.writeString(line2);
dest.writeInt(icon);
dest.writeInt(state);
+ if (Flags.modesApi()) {
+ dest.writeInt(this.source);
+ }
dest.writeInt(this.flags);
}
@Override
public String toString() {
- return new StringBuilder(Condition.class.getSimpleName()).append('[')
+ StringBuilder sb = new StringBuilder(Condition.class.getSimpleName()).append('[')
.append("state=").append(stateToString(state))
.append(",id=").append(id)
.append(",summary=").append(summary)
.append(",line1=").append(line1)
.append(",line2=").append(line2)
- .append(",icon=").append(icon)
- .append(",flags=").append(flags)
+ .append(",icon=").append(icon);
+ if (Flags.modesApi()) {
+ sb.append(",source=").append(sourceToString(source));
+ }
+ return sb.append(",flags=").append(flags)
.append(']').toString();
+
}
/** @hide */
@@ -171,6 +243,7 @@ public final class Condition implements Parcelable {
proto.write(ConditionProto.LINE_2, line2);
proto.write(ConditionProto.ICON, icon);
proto.write(ConditionProto.STATE, state);
+ // TODO: b/310644464 - Add source to dump.
proto.write(ConditionProto.FLAGS, flags);
proto.end(token);
@@ -184,6 +257,16 @@ public final class Condition implements Parcelable {
throw new IllegalArgumentException("state is invalid: " + state);
}
+ /** Provides a human-readable string version of the Source enum. */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static @NonNull String sourceToString(@Source int source) {
+ if (source == SOURCE_UNKNOWN) return "SOURCE_UNKNOWN";
+ if (source == SOURCE_USER_ACTION) return "SOURCE_USER_ACTION";
+ if (source == SOURCE_SCHEDULE) return "SOURCE_SCHEDULE";
+ if (source == SOURCE_CONTEXT) return "SOURCE_CONTEXT";
+ throw new IllegalArgumentException("source is invalid: " + source);
+ }
+
public static String relevanceToString(int flags) {
final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
@@ -197,17 +280,24 @@ public final class Condition implements Parcelable {
if (!(o instanceof Condition)) return false;
if (o == this) return true;
final Condition other = (Condition) o;
- return Objects.equals(other.id, id)
+ boolean finalEquals = Objects.equals(other.id, id)
&& Objects.equals(other.summary, summary)
&& Objects.equals(other.line1, line1)
&& Objects.equals(other.line2, line2)
&& other.icon == icon
&& other.state == state
&& other.flags == flags;
+ if (Flags.modesApi()) {
+ return finalEquals && other.source == source;
+ }
+ return finalEquals;
}
@Override
public int hashCode() {
+ if (Flags.modesApi()) {
+ return Objects.hash(id, summary, line1, line2, icon, state, source, flags);
+ }
return Objects.hash(id, summary, line1, line2, icon, state, flags);
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 759953e4aca4..92c516c38dcc 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -309,6 +309,7 @@ public abstract class NotificationListenerService extends Service {
REASON_ASSISTANT_CANCEL,
REASON_LOCKDOWN,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationCancelReason{};
/**
@@ -320,6 +321,7 @@ public abstract class NotificationListenerService extends Service {
FLAG_FILTER_TYPE_SILENT,
FLAG_FILTER_TYPE_ONGOING
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationFilterTypes {}
/**
* A flag value indicating that this notification listener can see conversation type
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
new file mode 100644
index 000000000000..db0b7ffa0913
--- /dev/null
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -0,0 +1,370 @@
+/*
+ * 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.notification;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * Represents the set of device effects (affecting display and device behavior in general) that
+ * are applied whenever an {@link android.app.AutomaticZenRule} is active.
+ */
+@FlaggedApi(Flags.FLAG_MODES_API)
+public final class ZenDeviceEffects implements Parcelable {
+
+ private final boolean mGrayscale;
+ private final boolean mSuppressAmbientDisplay;
+ private final boolean mDimWallpaper;
+ private final boolean mNightMode;
+
+ private final boolean mDisableAutoBrightness;
+ private final boolean mDisableTapToWake;
+ private final boolean mDisableTiltToWake;
+ private final boolean mDisableTouch;
+ private final boolean mMinimizeRadioUsage;
+ private final boolean mMaximizeDoze;
+
+ private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
+ boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
+ boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
+ boolean minimizeRadioUsage, boolean maximizeDoze) {
+ mGrayscale = grayscale;
+ mSuppressAmbientDisplay = suppressAmbientDisplay;
+ mDimWallpaper = dimWallpaper;
+ mNightMode = nightMode;
+ mDisableAutoBrightness = disableAutoBrightness;
+ mDisableTapToWake = disableTapToWake;
+ mDisableTiltToWake = disableTiltToWake;
+ mDisableTouch = disableTouch;
+ mMinimizeRadioUsage = minimizeRadioUsage;
+ mMaximizeDoze = maximizeDoze;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof final ZenDeviceEffects that)) return false;
+ if (obj == this) return true;
+
+ return this.mGrayscale == that.mGrayscale
+ && this.mSuppressAmbientDisplay == that.mSuppressAmbientDisplay
+ && this.mDimWallpaper == that.mDimWallpaper
+ && this.mNightMode == that.mNightMode
+ && this.mDisableAutoBrightness == that.mDisableAutoBrightness
+ && this.mDisableTapToWake == that.mDisableTapToWake
+ && this.mDisableTiltToWake == that.mDisableTiltToWake
+ && this.mDisableTouch == that.mDisableTouch
+ && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
+ && this.mMaximizeDoze == that.mMaximizeDoze;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
+ mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
+ mMinimizeRadioUsage, mMaximizeDoze);
+ }
+
+ @Override
+ public String toString() {
+ ArrayList<String> effects = new ArrayList<>(10);
+ if (mGrayscale) effects.add("grayscale");
+ if (mSuppressAmbientDisplay) effects.add("suppressAmbientDisplay");
+ if (mDimWallpaper) effects.add("dimWallpaper");
+ if (mNightMode) effects.add("nightMode");
+ if (mDisableAutoBrightness) effects.add("disableAutoBrightness");
+ if (mDisableTapToWake) effects.add("disableTapToWake");
+ if (mDisableTiltToWake) effects.add("disableTiltToWake");
+ if (mDisableTouch) effects.add("disableTouch");
+ if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
+ if (mMaximizeDoze) effects.add("maximizeDoze");
+ return "[" + String.join(", ", effects) + "]";
+ }
+
+ /**
+ * Whether the level of color saturation of the display should be set to minimum, effectively
+ * switching it to grayscale, while the rule is active.
+ */
+ public boolean shouldDisplayGrayscale() {
+ return mGrayscale;
+ }
+
+ /**
+ * Whether the ambient (always-on) display feature should be disabled while the rule is active.
+ * This will have no effect if the device doesn't support always-on display or if it's not
+ * generally enabled.
+ */
+ public boolean shouldSuppressAmbientDisplay() {
+ return mSuppressAmbientDisplay;
+ }
+
+ /** Whether the wallpaper should be dimmed while the rule is active. */
+ public boolean shouldDimWallpaper() {
+ return mDimWallpaper;
+ }
+
+ /** Whether night mode (aka dark theme) should be applied while the rule is active. */
+ public boolean shouldUseNightMode() {
+ return mNightMode;
+ }
+
+ /**
+ * Whether the display's automatic brightness adjustment should be disabled while the rule is
+ * active.
+ * @hide
+ */
+ public boolean shouldDisableAutoBrightness() {
+ return mDisableAutoBrightness;
+ }
+
+ /**
+ * Whether "tap to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTapToWake() {
+ return mDisableTapToWake;
+ }
+
+ /**
+ * Whether "tilt to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTiltToWake() {
+ return mDisableTiltToWake;
+ }
+
+ /**
+ * Whether touch interactions should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTouch() {
+ return mDisableTouch;
+ }
+
+ /**
+ * Whether radio (wi-fi, LTE, etc) traffic, and its attendant battery consumption, should be
+ * minimized while the rule is active.
+ * @hide
+ */
+ public boolean shouldMinimizeRadioUsage() {
+ return mMinimizeRadioUsage;
+ }
+
+ /**
+ * Whether Doze should be enhanced (e.g. with more aggresive activation, or less frequent
+ * maintenance windows) while the rule is active.
+ * @hide
+ */
+ public boolean shouldMaximizeDoze() {
+ return mMaximizeDoze;
+ }
+
+ /**
+ * Whether any of the effects are set up.
+ * @hide
+ */
+ public boolean hasEffects() {
+ return mGrayscale || mSuppressAmbientDisplay || mDimWallpaper || mNightMode
+ || mDisableAutoBrightness || mDisableTapToWake || mDisableTiltToWake
+ || mDisableTouch || mMinimizeRadioUsage || mMaximizeDoze;
+ }
+
+ /** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */
+ @NonNull
+ public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() {
+ @Override
+ public ZenDeviceEffects createFromParcel(Parcel in) {
+ return new ZenDeviceEffects(in.readBoolean(), in.readBoolean(), in.readBoolean(),
+ in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
+ in.readBoolean(), in.readBoolean(), in.readBoolean());
+ }
+
+ @Override
+ public ZenDeviceEffects[] newArray(int size) {
+ return new ZenDeviceEffects[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mGrayscale);
+ dest.writeBoolean(mSuppressAmbientDisplay);
+ dest.writeBoolean(mDimWallpaper);
+ dest.writeBoolean(mNightMode);
+ dest.writeBoolean(mDisableAutoBrightness);
+ dest.writeBoolean(mDisableTapToWake);
+ dest.writeBoolean(mDisableTiltToWake);
+ dest.writeBoolean(mDisableTouch);
+ dest.writeBoolean(mMinimizeRadioUsage);
+ dest.writeBoolean(mMaximizeDoze);
+ }
+
+ /** Builder class for {@link ZenDeviceEffects} objects. */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final class Builder {
+
+ private boolean mGrayscale;
+ private boolean mSuppressAmbientDisplay;
+ private boolean mDimWallpaper;
+ private boolean mNightMode;
+ private boolean mDisableAutoBrightness;
+ private boolean mDisableTapToWake;
+ private boolean mDisableTiltToWake;
+ private boolean mDisableTouch;
+ private boolean mMinimizeRadioUsage;
+ private boolean mMaximizeDoze;
+
+ /**
+ * Instantiates a new {@link ZenPolicy.Builder} with all effects set to default (disabled).
+ */
+ public Builder() {
+ }
+
+ /**
+ * Instantiates a new {@link ZenPolicy.Builder} with all effects set to their corresponding
+ * values in the supplied {@link ZenDeviceEffects}.
+ */
+ public Builder(@NonNull ZenDeviceEffects zenDeviceEffects) {
+ mGrayscale = zenDeviceEffects.shouldDisplayGrayscale();
+ mSuppressAmbientDisplay = zenDeviceEffects.shouldSuppressAmbientDisplay();
+ mDimWallpaper = zenDeviceEffects.shouldDimWallpaper();
+ mNightMode = zenDeviceEffects.shouldUseNightMode();
+ mDisableAutoBrightness = zenDeviceEffects.shouldDisableAutoBrightness();
+ mDisableTapToWake = zenDeviceEffects.shouldDisableTapToWake();
+ mDisableTiltToWake = zenDeviceEffects.shouldDisableTiltToWake();
+ mDisableTouch = zenDeviceEffects.shouldDisableTouch();
+ mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
+ mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
+ }
+
+ /**
+ * Sets whether the level of color saturation of the display should be set to minimum,
+ * effectively switching it to grayscale, while the rule is active.
+ */
+ @NonNull
+ public Builder setShouldDisplayGrayscale(boolean grayscale) {
+ mGrayscale = grayscale;
+ return this;
+ }
+
+ /**
+ * Sets whether the ambient (always-on) display feature should be disabled while the rule
+ * is active. This will have no effect if the device doesn't support always-on display or if
+ * it's not generally enabled.
+ */
+ @NonNull
+ public Builder setShouldSuppressAmbientDisplay(boolean suppressAmbientDisplay) {
+ mSuppressAmbientDisplay = suppressAmbientDisplay;
+ return this;
+ }
+
+ /** Sets whether the wallpaper should be dimmed while the rule is active. */
+ @NonNull
+ public Builder setShouldDimWallpaper(boolean dimWallpaper) {
+ mDimWallpaper = dimWallpaper;
+ return this;
+ }
+
+ /** Sets whether night mode (aka dark theme) should be applied while the rule is active. */
+ @NonNull
+ public Builder setShouldUseNightMode(boolean nightMode) {
+ mNightMode = nightMode;
+ return this;
+ }
+
+ /**
+ * Sets whether the display's automatic brightness adjustment should be disabled while the
+ * rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableAutoBrightness(boolean disableAutoBrightness) {
+ mDisableAutoBrightness = disableAutoBrightness;
+ return this;
+ }
+
+ /**
+ * Sets whether "tap to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTapToWake(boolean disableTapToWake) {
+ mDisableTapToWake = disableTapToWake;
+ return this;
+ }
+
+ /**
+ * Sets whether "tilt to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTiltToWake(boolean disableTiltToWake) {
+ mDisableTiltToWake = disableTiltToWake;
+ return this;
+ }
+
+ /**
+ * Sets whether touch interactions should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTouch(boolean disableTouch) {
+ mDisableTouch = disableTouch;
+ return this;
+ }
+
+ /**
+ * Sets whether radio (wi-fi, LTE, etc) traffic, and its attendant battery consumption,
+ * should be minimized while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldMinimizeRadioUsage(boolean minimizeRadioUsage) {
+ mMinimizeRadioUsage = minimizeRadioUsage;
+ return this;
+ }
+
+ /**
+ * Sets whether Doze should be enhanced (e.g. with more aggresive activation, or less
+ * frequent maintenance windows) while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldMaximizeDoze(boolean maximizeDoze) {
+ mMaximizeDoze = maximizeDoze;
+ return this;
+ }
+
+ /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
+ @NonNull
+ public ZenDeviceEffects build() {
+ return new ZenDeviceEffects(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper,
+ mNightMode, mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake,
+ mDisableTouch, mMinimizeRadioUsage, mMaximizeDoze);
+ }
+ }
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ff4dfc7cc079..f1d35b5b1185 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -25,6 +25,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
@@ -160,6 +161,7 @@ public class ZenModeConfig implements Parcelable {
private static final String CONDITION_ATT_LINE2 = "line2";
private static final String CONDITION_ATT_ICON = "icon";
private static final String CONDITION_ATT_STATE = "state";
+ private static final String CONDITION_ATT_SOURCE = "source";
private static final String CONDITION_ATT_FLAGS = "flags";
private static final String ZEN_POLICY_TAG = "zen_policy";
@@ -184,6 +186,18 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_ICON = "rule_icon";
private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
+ private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
+ private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
+ "zdeSuppressAmbientDisplay";
+ private static final String DEVICE_EFFECT_DIM_WALLPAPER = "zdeDimWallpaper";
+ private static final String DEVICE_EFFECT_USE_NIGHT_MODE = "zdeUseNightMode";
+ private static final String DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS = "zdeDisableAutoBrightness";
+ private static final String DEVICE_EFFECT_DISABLE_TAP_TO_WAKE = "zdeDisableTapToWake";
+ private static final String DEVICE_EFFECT_DISABLE_TILT_TO_WAKE = "zdeDisableTiltToWake";
+ private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch";
+ private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage";
+ private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze";
+
@UnsupportedAppUsage
public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
@@ -629,6 +643,7 @@ public class ZenModeConfig implements Parcelable {
rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
rt.zenPolicy = readZenPolicyXml(parser);
if (Flags.modesApi()) {
+ rt.zenDeviceEffects = readZenDeviceEffectsXml(parser);
rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false);
rt.iconResId = safeInt(parser, RULE_ATT_ICON, 0);
rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
@@ -666,6 +681,9 @@ public class ZenModeConfig implements Parcelable {
if (rule.zenPolicy != null) {
writeZenPolicyXml(rule.zenPolicy, out);
}
+ if (Flags.modesApi() && rule.zenDeviceEffects != null) {
+ writeZenDeviceEffectsXml(rule.zenDeviceEffects, out);
+ }
out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified);
if (Flags.modesApi()) {
out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation);
@@ -687,7 +705,12 @@ public class ZenModeConfig implements Parcelable {
final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
try {
- return new Condition(id, summary, line1, line2, icon, state, flags);
+ if (Flags.modesApi()) {
+ final int source = safeInt(parser, CONDITION_ATT_SOURCE, Condition.SOURCE_UNKNOWN);
+ return new Condition(id, summary, line1, line2, icon, state, source, flags);
+ } else {
+ return new Condition(id, summary, line1, line2, icon, state, flags);
+ }
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unable to read condition xml", e);
return null;
@@ -701,6 +724,9 @@ public class ZenModeConfig implements Parcelable {
out.attribute(null, CONDITION_ATT_LINE2, c.line2);
out.attributeInt(null, CONDITION_ATT_ICON, c.icon);
out.attributeInt(null, CONDITION_ATT_STATE, c.state);
+ if (Flags.modesApi()) {
+ out.attributeInt(null, CONDITION_ATT_SOURCE, c.source);
+ }
out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags);
}
@@ -850,6 +876,57 @@ public class ZenModeConfig implements Parcelable {
}
}
+ @Nullable
+ private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) {
+ ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(
+ safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false))
+ .setShouldSuppressAmbientDisplay(
+ safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false))
+ .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false))
+ .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false))
+ .setShouldDisableAutoBrightness(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false))
+ .setShouldDisableTapToWake(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false))
+ .setShouldDisableTiltToWake(
+ safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false))
+ .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false))
+ .setShouldMinimizeRadioUsage(
+ safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
+ .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
+ .build();
+
+ return deviceEffects.hasEffects() ? deviceEffects : null;
+ }
+
+ private static void writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects,
+ TypedXmlSerializer out) throws IOException {
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DISPLAY_GRAYSCALE,
+ deviceEffects.shouldDisplayGrayscale());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY,
+ deviceEffects.shouldSuppressAmbientDisplay());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DIM_WALLPAPER, deviceEffects.shouldDimWallpaper());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_MODE, deviceEffects.shouldUseNightMode());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS,
+ deviceEffects.shouldDisableAutoBrightness());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE,
+ deviceEffects.shouldDisableTapToWake());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE,
+ deviceEffects.shouldDisableTiltToWake());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TOUCH, deviceEffects.shouldDisableTouch());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
+ deviceEffects.shouldMinimizeRadioUsage());
+ writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
+ }
+
+ private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)
+ throws IOException {
+ if (value) {
+ out.attributeBoolean(null, att, true);
+ }
+ }
+
public static boolean isValidHour(int val) {
return val >= 0 && val < 24;
}
@@ -1004,6 +1081,8 @@ public class ZenModeConfig implements Parcelable {
priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
conversationSenders = getConversationSendersWithDefault(
zenPolicy.getPriorityConversationSenders(), conversationSenders);
+ } else {
+ conversationSenders = CONVERSATION_SENDERS_NONE;
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
@@ -1102,7 +1181,7 @@ public class ZenModeConfig implements Parcelable {
return (policy.suppressedVisualEffects & visualEffect) == 0;
}
- private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
+ private static int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
int defaultPolicySender) {
switch (senders) {
case ZenPolicy.PEOPLE_TYPE_ANYONE:
@@ -1116,7 +1195,7 @@ public class ZenModeConfig implements Parcelable {
}
}
- private int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
+ private static int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
int defaultPolicySender) {
switch (senders) {
case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
@@ -1744,6 +1823,8 @@ public class ZenModeConfig implements Parcelable {
// package name, only used for manual rules when they have turned DND on.
public String enabler;
public ZenPolicy zenPolicy;
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @Nullable public ZenDeviceEffects zenDeviceEffects;
public boolean modified; // rule has been modified from initial creation
public String pkg;
public int type = AutomaticZenRule.TYPE_UNKNOWN;
@@ -1773,6 +1854,9 @@ public class ZenModeConfig implements Parcelable {
enabler = source.readString();
}
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
+ if (Flags.modesApi()) {
+ zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
+ }
modified = source.readInt() == 1;
pkg = source.readString();
if (Flags.modesApi()) {
@@ -1817,6 +1901,9 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
+ if (Flags.modesApi()) {
+ dest.writeParcelable(zenDeviceEffects, 0);
+ }
dest.writeInt(modified ? 1 : 0);
dest.writeString(pkg);
if (Flags.modesApi()) {
@@ -1848,7 +1935,8 @@ public class ZenModeConfig implements Parcelable {
.append(",condition=").append(condition);
if (Flags.modesApi()) {
- sb.append(",allowManualInvocation=").append(allowManualInvocation)
+ sb.append(",deviceEffects=").append(zenDeviceEffects)
+ .append(",allowManualInvocation=").append(allowManualInvocation)
.append(",iconResId=").append(iconResId)
.append(",triggerDescription=").append(triggerDescription)
.append(",type=").append(type);
@@ -1906,6 +1994,7 @@ public class ZenModeConfig implements Parcelable {
if (Flags.modesApi()) {
return finalEquals
+ && Objects.equals(other.zenDeviceEffects, zenDeviceEffects)
&& other.allowManualInvocation == allowManualInvocation
&& other.iconResId == iconResId
&& Objects.equals(other.triggerDescription, triggerDescription)
@@ -1919,8 +2008,9 @@ public class ZenModeConfig implements Parcelable {
public int hashCode() {
if (Flags.modesApi()) {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, configurationActivity, pkg, id, enabler, zenPolicy, modified,
- allowManualInvocation, iconResId, triggerDescription, type);
+ component, configurationActivity, pkg, id, enabler, zenPolicy,
+ zenDeviceEffects, modified, allowManualInvocation, iconResId,
+ triggerDescription, type);
}
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index eb55e40f2c7b..f345d7cb88fd 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -452,11 +452,12 @@ public class ZenModeDiff {
public static final String FIELD_CREATION_TIME = "creationTime";
public static final String FIELD_ENABLER = "enabler";
public static final String FIELD_ZEN_POLICY = "zenPolicy";
+ public static final String FIELD_ZEN_DEVICE_EFFECTS = "zenDeviceEffects";
public static final String FIELD_MODIFIED = "modified";
public static final String FIELD_PKG = "pkg";
public static final String FIELD_ALLOW_MANUAL = "allowManualInvocation";
public static final String FIELD_ICON_RES = "iconResId";
- public static final String FIELD_TRIGGER = "triggerDescription";
+ public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription";
public static final String FIELD_TYPE = "type";
// NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule
@@ -534,19 +535,25 @@ public class ZenModeDiff {
if (!Objects.equals(from.pkg, to.pkg)) {
addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg));
}
- if (!Objects.equals(from.triggerDescription, to.triggerDescription)) {
- addField(FIELD_TRIGGER,
- new FieldDiff<>(from.triggerDescription, to.triggerDescription));
- }
- if (from.type != to.type) {
- addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type));
- }
- if (from.allowManualInvocation != to.allowManualInvocation) {
- addField(FIELD_ALLOW_MANUAL,
- new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation));
- }
- if (!Objects.equals(from.iconResId, to.iconResId)) {
- addField(FIELD_ICON_RES, new FieldDiff(from.iconResId, to.iconResId));
+ if (android.app.Flags.modesApi()) {
+ if (!Objects.equals(from.zenDeviceEffects, to.zenDeviceEffects)) {
+ addField(FIELD_ZEN_DEVICE_EFFECTS,
+ new FieldDiff<>(from.zenDeviceEffects, to.zenDeviceEffects));
+ }
+ if (!Objects.equals(from.triggerDescription, to.triggerDescription)) {
+ addField(FIELD_TRIGGER_DESCRIPTION,
+ new FieldDiff<>(from.triggerDescription, to.triggerDescription));
+ }
+ if (from.type != to.type) {
+ addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type));
+ }
+ if (from.allowManualInvocation != to.allowManualInvocation) {
+ addField(FIELD_ALLOW_MANUAL,
+ new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation));
+ }
+ if (!Objects.equals(from.iconResId, to.iconResId)) {
+ addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResId, to.iconResId));
+ }
}
}
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 9167153a0ef5..6da3206708a7 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -66,6 +66,7 @@ public class PersistentDataBlockManager {
*/
public static final int FLASH_LOCK_LOCKED = 1;
+ /** @removed mistakenly exposed previously */
@IntDef(prefix = { "FLASH_LOCK_" }, value = {
FLASH_LOCK_UNKNOWN,
FLASH_LOCK_LOCKED,
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index ff6dffdaaf5d..1e08fd8061e0 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -85,6 +85,7 @@ public final class HotwordDetectedResult implements Parcelable {
CONFIDENCE_LEVEL_HIGH,
CONFIDENCE_LEVEL_VERY_HIGH
})
+ @Retention(RetentionPolicy.SOURCE)
@interface HotwordConfidenceLevelValue {
}
diff --git a/core/java/android/service/voice/HotwordRejectedResult.java b/core/java/android/service/voice/HotwordRejectedResult.java
index 7b3f47d04e48..26c1ca428c3b 100644
--- a/core/java/android/service/voice/HotwordRejectedResult.java
+++ b/core/java/android/service/voice/HotwordRejectedResult.java
@@ -22,6 +22,9 @@ import android.os.Parcelable;
import com.android.internal.util.DataClass;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a result supporting the rejected hotword trigger.
*
@@ -57,6 +60,7 @@ public final class HotwordRejectedResult implements Parcelable {
CONFIDENCE_LEVEL_MEDIUM,
CONFIDENCE_LEVEL_HIGH
})
+ @Retention(RetentionPolicy.SOURCE)
@interface HotwordConfidenceLevelValue {
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 04ae0aff10d0..54116a2749e0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -273,8 +273,8 @@ public abstract class WallpaperService extends Service {
int mCurHeight;
float mZoom = 0f;
int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS
- | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+ int mWindowPrivateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
int mCurWindowPrivateFlags = mWindowPrivateFlags;
Rect mPreviewSurfacePosition;
@@ -2289,7 +2289,7 @@ public abstract class WallpaperService extends Service {
mInputEventReceiver = null;
}
- mSession.remove(mWindow);
+ mSession.remove(mWindow.asBinder());
} catch (RemoteException e) {
}
mSurfaceHolder.mSurface.release();
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 35834fd3886f..e6fcc0c391b2 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -38,6 +38,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.CloseGuard;
import android.util.Log;
import android.util.Slog;
@@ -46,6 +47,7 @@ import com.android.internal.R;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
@@ -59,6 +61,9 @@ import java.util.concurrent.LinkedBlockingQueue;
* {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
* invoked only from the main application thread.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p>The implementation of this API is likely to stream audio to remote servers to perform speech
* recognition. As such this API is not intended to be used for continuous recognition, which would
* consume a significant amount of battery and bandwidth.
@@ -301,6 +306,8 @@ public class SpeechRecognizer {
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
/** Context with which the manager was created */
private final Context mContext;
@@ -366,9 +373,7 @@ public class SpeechRecognizer {
* {@link #createSpeechRecognizer} static factory method
*/
private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
- mContext = context;
- mServiceComponent = serviceComponent;
- mOnDevice = false;
+ this(context, serviceComponent, false);
}
/**
@@ -376,9 +381,17 @@ public class SpeechRecognizer {
* {@link #createOnDeviceSpeechRecognizer} static factory method
*/
private SpeechRecognizer(final Context context, boolean onDevice) {
+ this(context, null, onDevice);
+ }
+
+ private SpeechRecognizer(
+ final Context context,
+ final ComponentName serviceComponent,
+ final boolean onDevice) {
mContext = context;
- mServiceComponent = null;
+ mServiceComponent = serviceComponent;
mOnDevice = onDevice;
+ mCloseGuard.open("SpeechRecognizer#destroy()");
}
/**
@@ -418,6 +431,9 @@ public class SpeechRecognizer {
* command to the created {@code SpeechRecognizer}, otherwise no notifications will be
* received.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition
* service requires <queries> element to be added to the manifest file:
* <pre>{@code
@@ -445,6 +461,9 @@ public class SpeechRecognizer {
* Use this version of the method to specify a specific service to direct this
* {@link SpeechRecognizer} to.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p><strong>Important</strong>: before calling this method, please check via
* {@link android.content.pm.PackageManager#queryIntentServices(Intent, int)} that {@code
* serviceComponent} actually exists and provides
@@ -486,6 +505,9 @@ public class SpeechRecognizer {
* before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
* notifications will be received.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* @param context in which to create {@code SpeechRecognizer}
* @return a new on-device {@code SpeechRecognizer}.
* @throws UnsupportedOperationException iff {@link #isOnDeviceRecognitionAvailable(Context)}
@@ -870,7 +892,7 @@ public class SpeechRecognizer {
}
private boolean checkOpenConnection() {
- if (mService != null) {
+ if (mService != null && mService.asBinder().isBinderAlive()) {
return true;
}
mListener.onError(ERROR_CLIENT);
@@ -886,17 +908,32 @@ public class SpeechRecognizer {
/** Destroys the {@code SpeechRecognizer} object. */
public void destroy() {
- if (mService != null) {
- try {
- mService.cancel(mListener, /*isShutdown*/ true);
- } catch (final Exception e) {
- // Not important
+ try {
+ if (mService != null) {
+ try {
+ mService.cancel(mListener, /*isShutdown*/ true);
+ } catch (final Exception e) {
+ // Not important
+ }
}
+
+ mService = null;
+ mPendingTasks.clear();
+ mListener.mInternalListener = null;
+ mCloseGuard.close();
+ } finally {
+ Reference.reachabilityFence(this);
}
+ }
- mService = null;
- mPendingTasks.clear();
- mListener.mInternalListener = null;
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ destroy();
+ } finally {
+ super.finalize();
+ }
}
/** Establishes a connection to system server proxy and initializes the session. */
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 77e616b358cb..2105420b84cd 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -118,6 +118,7 @@ public class StaticLayout extends Layout {
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
b.mLineBreakConfig = LineBreakConfig.NONE;
+ b.mMinimumFontMetrics = null;
return b;
}
@@ -130,6 +131,7 @@ public class StaticLayout extends Layout {
b.mText = null;
b.mLeftIndents = null;
b.mRightIndents = null;
+ b.mMinimumFontMetrics = null;
sPool.release(b);
}
@@ -139,6 +141,7 @@ public class StaticLayout extends Layout {
mPaint = null;
mLeftIndents = null;
mRightIndents = null;
+ mMinimumFontMetrics = null;
}
public Builder setText(CharSequence source) {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index c0a5629f9220..6ea462eb969f 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -68,6 +68,7 @@ import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.text.style.UpdateAppearance;
+import android.util.EmptyArray;
import android.util.Log;
import android.util.Printer;
import android.view.View;
@@ -141,9 +142,9 @@ public class TextUtils {
return (method == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
}
-
private TextUtils() { /* cannot be instantiated */ }
+ @android.ravenwood.annotation.RavenwoodKeep
public static void getChars(CharSequence s, int start, int end,
char[] dest, int destoff) {
Class<? extends CharSequence> c = s.getClass();
@@ -162,10 +163,12 @@ public class TextUtils {
}
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch) {
return indexOf(s, ch, 0);
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start) {
Class<? extends CharSequence> c = s.getClass();
@@ -175,6 +178,7 @@ public class TextUtils {
return indexOf(s, ch, start, s.length());
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start, int end) {
Class<? extends CharSequence> c = s.getClass();
@@ -212,10 +216,12 @@ public class TextUtils {
return -1;
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch) {
return lastIndexOf(s, ch, s.length() - 1);
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch, int last) {
Class<? extends CharSequence> c = s.getClass();
@@ -225,6 +231,7 @@ public class TextUtils {
return lastIndexOf(s, ch, 0, last);
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch,
int start, int last) {
if (last < 0)
@@ -270,14 +277,17 @@ public class TextUtils {
return -1;
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle) {
return indexOf(s, needle, 0, s.length());
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle, int start) {
return indexOf(s, needle, start, s.length());
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle,
int start, int end) {
int nlen = needle.length();
@@ -305,6 +315,7 @@ public class TextUtils {
return -1;
}
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean regionMatches(CharSequence one, int toffset,
CharSequence two, int ooffset,
int len) {
@@ -337,6 +348,7 @@ public class TextUtils {
* in that it does not preserve any style runs in the source sequence,
* allowing a more efficient implementation.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String substring(CharSequence source, int start, int end) {
if (source instanceof String)
return ((String) source).substring(start, end);
@@ -409,6 +421,7 @@ public class TextUtils {
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is an empty array, an empty string will be returned.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Object[] tokens) {
final int length = tokens.length;
if (length == 0) {
@@ -432,6 +445,7 @@ public class TextUtils {
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is empty, an empty string will be returned.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Iterable tokens) {
final Iterator<?> it = tokens.iterator();
if (!it.hasNext()) {
@@ -464,9 +478,10 @@ public class TextUtils {
*
* @throws NullPointerException if expression or text is null
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, String expression) {
if (text.length() == 0) {
- return EMPTY_STRING_ARRAY;
+ return EmptyArray.STRING;
} else {
return text.split(expression, -1);
}
@@ -489,9 +504,10 @@ public class TextUtils {
*
* @throws NullPointerException if expression or text is null
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, Pattern pattern) {
if (text.length() == 0) {
- return EMPTY_STRING_ARRAY;
+ return EmptyArray.STRING;
} else {
return pattern.split(text, -1);
}
@@ -526,6 +542,7 @@ public class TextUtils {
* be returned for the empty string after that delimeter. That is, splitting <tt>"a,b,"</tt> on
* comma will return <tt>"a", "b"</tt>, not <tt>"a", "b", ""</tt>.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class SimpleStringSplitter implements StringSplitter, Iterator<String> {
private String mString;
private char mDelimiter;
@@ -589,26 +606,31 @@ public class TextUtils {
* @param str the string to be examined
* @return true if str is null or zero length
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isEmpty(@Nullable CharSequence str) {
return str == null || str.length() == 0;
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String nullIfEmpty(@Nullable String str) {
return isEmpty(str) ? null : str;
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String emptyIfNull(@Nullable String str) {
return str == null ? "" : str;
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String firstNotEmpty(@Nullable String a, @NonNull String b) {
return !isEmpty(a) ? a : Preconditions.checkStringNotEmpty(b);
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodKeep
public static int length(@Nullable String s) {
return s != null ? s.length() : 0;
}
@@ -617,6 +639,7 @@ public class TextUtils {
* @return interned string if it's null.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String safeIntern(String s) {
return (s != null) ? s.intern() : null;
}
@@ -626,6 +649,7 @@ public class TextUtils {
* spaces and ASCII control characters were trimmed from the start and end,
* as by {@link String#trim}.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getTrimmedLength(CharSequence s) {
int len = s.length();
@@ -650,6 +674,7 @@ public class TextUtils {
* @param b second CharSequence to check
* @return true if a and b are equal
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean equals(CharSequence a, CharSequence b) {
if (a == b) return true;
int length;
@@ -1679,6 +1704,7 @@ public class TextUtils {
return true;
}
+ @android.ravenwood.annotation.RavenwoodReplace
/* package */ static char[] obtain(int len) {
char[] buf;
@@ -1693,6 +1719,11 @@ public class TextUtils {
return buf;
}
+ /* package */ static char[] obtain$ravenwood(int len) {
+ return new char[len];
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
/* package */ static void recycle(char[] temp) {
if (temp.length > 1000)
return;
@@ -1702,11 +1733,16 @@ public class TextUtils {
}
}
+ /* package */ static void recycle$ravenwood(char[] temp) {
+ // Handled by typical GC
+ }
+
/**
* Html-encode the string.
* @param s the string to be encoded
* @return the encoded string
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String htmlEncode(String s) {
StringBuilder sb = new StringBuilder();
char c;
@@ -1793,6 +1829,7 @@ public class TextUtils {
/**
* Returns whether the given CharSequence contains any printable characters.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(CharSequence str) {
final int len = str.length();
for (int cp, i=0; i<len; i+=Character.charCount(cp)) {
@@ -1819,6 +1856,7 @@ public class TextUtils {
* @deprecated Use {@link #isGraphic(CharSequence)} instead.
*/
@Deprecated
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(char c) {
int gc = Character.getType(c);
return gc != Character.CONTROL
@@ -1833,6 +1871,7 @@ public class TextUtils {
/**
* Returns whether the given CharSequence contains only digits.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isDigitsOnly(CharSequence str) {
final int len = str.length();
for (int cp, i = 0; i < len; i += Character.charCount(cp)) {
@@ -1847,6 +1886,7 @@ public class TextUtils {
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAscii(final char c) {
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
@@ -1857,6 +1897,7 @@ public class TextUtils {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAsciiOnly(final CharSequence str) {
final int len = str.length();
for (int i = 0; i < len; i++) {
@@ -1908,6 +1949,7 @@ public class TextUtils {
* {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
* {@link #CAP_MODE_SENTENCES}.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getCapsMode(CharSequence cs, int off, int reqModes) {
if (off < 0) {
return 0;
@@ -2153,6 +2195,7 @@ public class TextUtils {
* match the supported grammar described above.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static @NonNull String formatSimple(@NonNull String format, Object... args) {
final StringBuilder sb = new StringBuilder(format);
int j = 0;
@@ -2342,6 +2385,7 @@ public class TextUtils {
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isNewline(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
@@ -2349,16 +2393,19 @@ public class TextUtils {
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespace(int codePoint) {
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespaceExceptNewline(int codePoint) {
return isWhitespace(codePoint) && !isNewline(codePoint);
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPunctuation(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.CONNECTOR_PUNCTUATION
@@ -2608,6 +2655,4 @@ public class TextUtils {
private static Object sLock = new Object();
private static char[] sTemp = null;
-
- private static String[] EMPTY_STRING_ARRAY = new String[]{};
}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 43c38f319713..a1885ae616e1 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -75,3 +75,10 @@ flag {
description: "A feature flag that implements line break word style auto."
bug: "280005585"
}
+
+flag {
+ name: "inter_character_justification"
+ namespace: "text"
+ description: "A feature flag that implement inter character justification."
+ bug: "283193133"
+}
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index cc33af32ba93..10905e1b1908 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public enum DataUnit {
KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
MEGABYTES { @Override public long toBytes(long v) { return v * 1_000_000; } },
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 4654dbfa9531..d2c5975ea356 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -48,6 +48,9 @@ import java.util.regex.Pattern;
* They carry a payload of one or more int, long, or String values. The
* event-log-tags file defines the payload contents for each type code.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
+ "com.android.hoststubgen.nativesubstitution.EventLog_host")
public class EventLog {
/** @hide */ public EventLog() {}
@@ -416,6 +419,7 @@ public class EventLog {
/**
* Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
private static synchronized void readTagsFile() {
if (sTagCodes != null && sTagNames != null) return;
@@ -441,8 +445,7 @@ public class EventLog {
try {
int num = Integer.parseInt(m.group(1));
String name = m.group(2);
- sTagCodes.put(name, num);
- sTagNames.put(num, name);
+ registerTagLocked(num, name);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
}
@@ -454,4 +457,20 @@ public class EventLog {
try { if (reader != null) reader.close(); } catch (IOException e) {}
}
}
+
+ private static void registerTagLocked(int num, String name) {
+ sTagCodes.put(name, num);
+ sTagNames.put(num, name);
+ }
+
+ private static synchronized void readTagsFile$ravenwood() {
+ // TODO: restore parsing logic once we carry into runtime
+ sTagCodes = new HashMap<String, Integer>();
+ sTagNames = new HashMap<Integer, String>();
+
+ // Hard-code a few common tags
+ registerTagLocked(524288, "sysui_action");
+ registerTagLocked(524290, "sysui_count");
+ registerTagLocked(524291, "sysui_histogram");
+ }
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 766e924c994e..b91a8789a181 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -163,12 +163,6 @@ public class FeatureFlagUtils {
public static final String SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS =
"settings_remoteauth_enrollment";
- /** Flag to enable/disable entire page in Accessibility -> Hearing aids
- * @hide
- */
- public static final String SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE =
- "settings_accessibility_hearing_aid_page";
-
/**
* Flag to enable/disable preferring the AccessibilityMenu service in the system.
* @hide
@@ -244,7 +238,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
- DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, "true");
DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index c04a71c4d31b..413ae1f5cbcf 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class IntArray implements Cloneable {
private static final int MIN_CAPACITY_INCREMENT = 12;
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 3101c0da6986..4c7ef08ddfcb 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -30,6 +30,7 @@ import java.util.Arrays;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LongArray implements Cloneable {
private static final int MIN_CAPACITY_INCREMENT = 12;
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3aeeccaba250..c0ceb9ea8204 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -31,6 +31,7 @@ import android.os.Build;
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Slog {
private Slog() {
@@ -216,6 +217,7 @@ public final class Slog {
* @see Log#wtf(String, String)
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodThrow
public static int wtf(@Nullable String tag, @NonNull String msg) {
return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, false, true);
}
@@ -223,6 +225,7 @@ public final class Slog {
/**
* Similar to {@link #wtf(String, String)}, but does not output anything to the log.
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public static void wtfQuiet(@Nullable String tag, @NonNull String msg) {
Log.wtfQuiet(Log.LOG_ID_SYSTEM, tag, msg, true);
}
@@ -241,6 +244,7 @@ public final class Slog {
* @see Log#wtfStack(String, String)
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @android.ravenwood.annotation.RavenwoodThrow
public static int wtfStack(@Nullable String tag, @NonNull String msg) {
return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true, true);
}
@@ -259,6 +263,7 @@ public final class Slog {
*
* @see Log#wtf(String, Throwable)
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr, false, true);
}
@@ -279,6 +284,7 @@ public final class Slog {
* @see Log#wtf(String, String, Throwable)
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodThrow
public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr, false, true);
}
diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING
index 0ae1c1593366..c681f86ce439 100644
--- a/core/java/android/util/TEST_MAPPING
+++ b/core/java/android/util/TEST_MAPPING
@@ -24,5 +24,11 @@
],
"file_patterns": ["Xml"]
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsUtilTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index d06b0ce1a2d8..bff8db13ad14 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -41,6 +41,8 @@ import java.util.List;
/**
* A class containing utility methods related to time zones.
*/
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@android.ravenwood.annotation.RavenwoodKeepStaticInitializer
public class TimeUtils {
/** @hide */ public TimeUtils() {}
/** {@hide} */
@@ -180,6 +182,7 @@ public class TimeUtils {
private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
+ @android.ravenwood.annotation.RavenwoodKeep
static private int accumField(int amt, int suffix, boolean always, int zeropad) {
if (amt > 999) {
int num = 0;
@@ -202,6 +205,7 @@ public class TimeUtils {
return 0;
}
+ @android.ravenwood.annotation.RavenwoodKeep
static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos,
boolean always, int zeropad) {
if (always || amt > 0) {
@@ -242,6 +246,7 @@ public class TimeUtils {
return pos;
}
+ @android.ravenwood.annotation.RavenwoodKeep
private static int formatDurationLocked(long duration, int fieldLen) {
if (sFormatStr.length < fieldLen) {
sFormatStr = new char[fieldLen];
@@ -314,6 +319,7 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long duration, StringBuilder builder) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, 0);
@@ -322,6 +328,7 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long duration, StringBuilder builder, int fieldLen) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, fieldLen);
@@ -331,6 +338,7 @@ public class TimeUtils {
/** @hide Just for debugging; not internationalized. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, fieldLen);
@@ -340,6 +348,7 @@ public class TimeUtils {
/** @hide Just for debugging; not internationalized. */
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static String formatDuration(long duration) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, 0);
@@ -349,11 +358,13 @@ public class TimeUtils {
/** @hide Just for debugging; not internationalized. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long duration, PrintWriter pw) {
formatDuration(duration, pw, 0);
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long time, long now, StringBuilder sb) {
if (time == 0) {
sb.append("--");
@@ -363,6 +374,7 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static void formatDuration(long time, long now, PrintWriter pw) {
if (time == 0) {
pw.print("--");
@@ -372,16 +384,19 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String formatUptime(long time) {
return formatTime(time, SystemClock.uptimeMillis());
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String formatRealtime(long time) {
return formatTime(time, SystemClock.elapsedRealtime());
}
/** @hide Just for debugging; not internationalized. */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String formatTime(long time, long referenceTime) {
long diff = time - referenceTime;
if (diff > 0) {
@@ -402,6 +417,7 @@ public class TimeUtils {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodKeep
public static String logTimeOfDay(long millis) {
Calendar c = Calendar.getInstance();
if (millis >= 0) {
@@ -413,6 +429,7 @@ public class TimeUtils {
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodKeep
public static String formatForLogging(long millis) {
if (millis <= 0) {
return "unknown";
@@ -426,6 +443,7 @@ public class TimeUtils {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void dumpTime(PrintWriter pw, long time) {
pw.print(sDumpDateFormat.format(new Date(time)));
}
@@ -457,6 +475,7 @@ public class TimeUtils {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) {
pw.print(sDumpDateFormat.format(new Date(time)));
if (time == now) {
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index 33058d84b2e5..2a33caaf7e28 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -26,6 +26,7 @@ import com.android.internal.util.ArtBinaryXmlPullParser;
import com.android.internal.util.ArtBinaryXmlSerializer;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.BinaryXmlPullParser;
import com.android.modules.utils.BinaryXmlSerializer;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -38,6 +39,7 @@ import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
@@ -115,6 +117,7 @@ public class Xml {
/**
* Returns a new pull parser with namespace support.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static XmlPullParser newPullParser() {
try {
XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
@@ -126,6 +129,12 @@ public class Xml {
}
}
+ /** @hide */
+ public static XmlPullParser newPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link TypedXmlPullParser} which is optimized for use
* inside the system, typically by supporting only a basic set of features.
@@ -136,10 +145,17 @@ public class Xml {
* @hide
*/
@SuppressWarnings("AndroidFrameworkEfficientXml")
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser newFastPullParser() {
return XmlUtils.makeTyped(newPullParser());
}
+ /** @hide */
+ public static TypedXmlPullParser newFastPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link XmlPullParser} that reads XML documents using a
* custom binary wire protocol which benchmarking has shown to be 8.5x
@@ -148,10 +164,17 @@ public class Xml {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser newBinaryPullParser() {
return new ArtBinaryXmlPullParser();
}
+ /** @hide */
+ public static TypedXmlPullParser newBinaryPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link XmlPullParser} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -166,6 +189,7 @@ public class Xml {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
throws IOException {
final byte[] magic = new byte[4];
@@ -198,13 +222,33 @@ public class Xml {
return xml;
}
+ /** @hide */
+ public static @NonNull TypedXmlPullParser resolvePullParser$ravenwood(@NonNull InputStream in)
+ throws IOException {
+ // TODO: remove once we're linking against libcore
+ final TypedXmlPullParser xml = new BinaryXmlPullParser();
+ try {
+ xml.setInput(in, StandardCharsets.UTF_8.name());
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ return xml;
+ }
+
/**
* Creates a new xml serializer.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static XmlSerializer newSerializer() {
return XmlObjectFactory.newXmlSerializer();
}
+ /** @hide */
+ public static XmlSerializer newSerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -215,10 +259,17 @@ public class Xml {
* @hide
*/
@SuppressWarnings("AndroidFrameworkEfficientXml")
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer newFastSerializer() {
return XmlUtils.makeTyped(new FastXmlSerializer());
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer newFastSerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} that writes XML documents using a
* custom binary wire protocol which benchmarking has shown to be 4.4x
@@ -227,10 +278,17 @@ public class Xml {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer newBinarySerializer() {
return new ArtBinaryXmlSerializer();
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer newBinarySerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -245,6 +303,7 @@ public class Xml {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out)
throws IOException {
final TypedXmlSerializer xml;
@@ -257,6 +316,15 @@ public class Xml {
return xml;
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer resolveSerializer$ravenwood(@NonNull OutputStream out)
+ throws IOException {
+ // TODO: remove once we're linking against libcore
+ final TypedXmlSerializer xml = new BinaryXmlSerializer();
+ xml.setOutput(out, StandardCharsets.UTF_8.name());
+ return xml;
+ }
+
/**
* Copy the first XML document into the second document.
* <p>
diff --git a/core/java/android/util/proto/TEST_MAPPING b/core/java/android/util/proto/TEST_MAPPING
index 5b9874153de3..126174374896 100644
--- a/core/java/android/util/proto/TEST_MAPPING
+++ b/core/java/android/util/proto/TEST_MAPPING
@@ -6,5 +6,11 @@
{
"name": "CtsProtoTestCases"
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsProtoTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6c3b8ab19792..17bbee6d020f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -114,8 +114,6 @@ interface IWindowManager
IWindowSession openSession(in IWindowSessionCallback callback);
- boolean useBLAST();
-
@UnsupportedAppUsage
void getInitialDisplaySize(int displayId, out Point size);
@UnsupportedAppUsage
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 02e97dae70e2..dbacca5def51 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -59,8 +59,15 @@ interface IWindowSession {
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out InsetsState insetsState,
out Rect attachedFrame, out float[] sizeCompatScale);
- @UnsupportedAppUsage
- void remove(IWindow window);
+
+ /**
+ * Removes a clientToken from WMS, which includes unlinking the input channel.
+ *
+ * @param clientToken The token that should be removed. This will normally be the IWindow token
+ * for a standard window. It can also be the generic clientToken that was used when calling
+ * grantInputChannel
+ */
+ void remove(IBinder clientToken);
/**
* Change the parameters of a window. You supply the
@@ -296,9 +303,11 @@ interface IWindowSession {
/**
* Request the server to call setInputWindowInfo on a given Surface, and return
- * an input channel where the client can receive input.
+ * an input channel where the client can receive input. For windows, the clientToken should be
+ * the IWindow binder object. For other requests, the token can be any unique IBinder token to
+ * be used as unique identifier.
*/
- void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
+ void grantInputChannel(int displayId, in SurfaceControl surface, in IBinder clientToken,
in IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type,
in IBinder windowToken, in IBinder focusGrantToken, String inputHandleName,
out InputChannel outInputChannel);
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 492c9384a969..19e6836ed261 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -278,14 +278,4 @@ public abstract class InputEventReceiver {
writer.println(prefix + " mSeqMap: " + mSeqMap);
writer.println(prefix + " mReceiverPtr:\n" + nativeDump(mReceiverPtr, prefix + " "));
}
-
- /**
- * Factory for InputEventReceiver
- */
- public interface Factory {
- /**
- * Create a new InputReceiver for a given inputChannel
- */
- InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper);
- }
}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 45b3fdd7e5bc..59ec60545d6d 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -26,6 +26,7 @@ import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.IBinder;
import android.os.InputConfig;
+import android.util.Size;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -82,15 +83,11 @@ public final class InputWindowHandle {
public IBinder token;
/**
- * The {@link IWindow} handle if InputWindowHandle is associated with a window, null otherwise.
+ * The {@link IBinder} handle if InputWindowHandle is associated with a client token,
+ * normally the IWindow token, null otherwise.
*/
@Nullable
private IBinder windowToken;
- /**
- * Used to cache IWindow from the windowToken so we don't need to convert every time getWindow
- * is called.
- */
- private IWindow window;
// The window name.
public String name;
@@ -106,6 +103,9 @@ public final class InputWindowHandle {
// Window frame.
public final Rect frame = new Rect();
+ // The real size of the content, excluding any crop. If no buffer is rendered, this is 0,0
+ public Size contentSize = new Size(0, 0);
+
public int surfaceInset;
// Global scaling factor applied to touch events when they are dispatched
@@ -157,6 +157,11 @@ public final class InputWindowHandle {
public Matrix transform;
/**
+ * The alpha value returned from SurfaceFlinger. This will be ignored if passed as input data.
+ */
+ public float alpha;
+
+ /**
* The input token for the window to which focus should be transferred when this input window
* can be successfully focused. If null, this input window will not transfer its focus to
* any other window.
@@ -177,7 +182,6 @@ public final class InputWindowHandle {
inputApplicationHandle = new InputApplicationHandle(other.inputApplicationHandle);
token = other.token;
windowToken = other.windowToken;
- window = other.window;
name = other.name;
layoutParamsFlags = other.layoutParamsFlags;
layoutParamsType = other.layoutParamsType;
@@ -199,6 +203,8 @@ public final class InputWindowHandle {
transform.set(other.transform);
}
focusTransferTarget = other.focusTransferTarget;
+ contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight());
+ alpha = other.alpha;
}
@Override
@@ -211,6 +217,8 @@ public final class InputWindowHandle {
.append(", windowToken=").append(windowToken)
.append(", displayId=").append(displayId)
.append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0)
+ .append(", contentSize=").append(contentSize)
+ .append(", alpha=").append(alpha)
.toString();
}
@@ -243,23 +251,14 @@ public final class InputWindowHandle {
touchableRegionSurfaceControl = new WeakReference<>(bounds);
}
- public void setWindowToken(IWindow iwindow) {
- windowToken = iwindow.asBinder();
- window = iwindow;
+ public void setWindowToken(IBinder iwindow) {
+ windowToken = iwindow;
}
public @Nullable IBinder getWindowToken() {
return windowToken;
}
- public IWindow getWindow() {
- if (window != null) {
- return window;
- }
- window = IWindow.Stub.asInterface(windowToken);
- return window;
- }
-
/**
* Set the provided inputConfig flag values.
* @param inputConfig the flag values to change
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 1acc384b18da..cabab6c80bf5 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -49,8 +49,9 @@ public class InsetsSource implements Parcelable {
/** The insets source ID of IME */
public static final int ID_IME = createId(null, 0, ime());
+
/** The insets source ID of the IME caption bar ("fake" IME navigation bar). */
- static final int ID_IME_CAPTION_BAR =
+ public static final int ID_IME_CAPTION_BAR =
InsetsSource.createId(null /* owner */, 1 /* index */, captionBar());
/**
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index b17d2d1800e5..c6601e8d3085 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -2065,6 +2065,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT:
+ case KeyEvent.KEYCODE_STEM_PRIMARY:
return true;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 451a71b71e3f..8befe8a2be85 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -436,13 +436,16 @@ public final class SurfaceControl implements Parcelable {
// Jank due to unknown reasons.
public static final int UNKNOWN = 0x80;
- public JankData(long frameVsyncId, @JankType int jankType) {
+ public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs) {
this.frameVsyncId = frameVsyncId;
this.jankType = jankType;
+ this.frameIntervalNs = frameIntervalNs;
+
}
public final long frameVsyncId;
public final @JankType int jankType;
+ public final long frameIntervalNs;
}
/**
@@ -3280,7 +3283,8 @@ public final class SurfaceControl implements Parcelable {
"setCrop", this, sc, "crop=" + crop);
}
if (crop != null) {
- Preconditions.checkArgument(crop.isValid(), "Crop isn't valid.");
+ Preconditions.checkArgument(crop.isValid(), "Crop " + crop
+ + " isn't valid");
nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
crop.left, crop.top, crop.right, crop.bottom);
} else {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0ae14a2fdb30..a44a95a1677f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1523,15 +1523,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (mSurfaceControl == null) return;
mRTLastReportedPosition.set(left, top, right, bottom);
+ final float postScaleX = mRTLastReportedPosition.width()
+ / (float) mRtSurfaceWidth;
+ final float postScaleY = mRTLastReportedPosition.height()
+ / (float) mRtSurfaceHeight;
onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl,
mRTLastReportedPosition.left /*positionLeft*/,
mRTLastReportedPosition.top /*positionTop*/,
- mRTLastReportedPosition.width()
- / (float) mRtSurfaceWidth /*postScaleX*/,
- mRTLastReportedPosition.height()
- / (float) mRtSurfaceHeight /*postScaleY*/);
-
- mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
+ postScaleX, postScaleY);
+
+ mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY),
+ (int) Math.ceil(clipRight / postScaleX),
+ (int) Math.ceil(clipBottom / postScaleY));
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
+ + "from scale %f, %f", mRTLastSetCrop.left,
+ mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom,
+ postScaleX, postScaleY));
+ }
mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop);
if (mRTLastSetCrop.isEmpty()) {
mPositionChangedTransaction.hide(mSurfaceControl);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a478b3c40df5..d591f896d99a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19,8 +19,10 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.Surface.FRAME_RATE_CATEGORY_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.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;
@@ -28,6 +30,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
@@ -5521,6 +5524,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
+ // The preferred frame rate of the view that is mainly used for
+ // touch boosting, view velocity handling, and TextureView.
+ private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
+
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
+ @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)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -120;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -33015,9 +33033,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return (float) viewSize / screenSize;
}
- private int calculateFrameRateCategory() {
- float sizePercentage = getSizePercentage();
-
+ private int calculateFrameRateCategory(float sizePercentage) {
if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
return FRAME_RATE_CATEGORY_LOW;
} else {
@@ -33028,9 +33044,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private void votePreferredFrameRate() {
// use toolkitSetFrameRate flag to gate the change
ViewRootImpl viewRootImpl = getViewRootImpl();
+ float sizePercentage = getSizePercentage();
+ int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
if (sToolkitSetFrameRateReadOnlyFlagValue && viewRootImpl != null
- && getSizePercentage() > 0) {
- viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
+ && sizePercentage > 0) {
+ if (mPreferredFrameRate < 0) {
+ if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_LOW;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
+ }
+ } else {
+ viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+ }
+ viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
}
}
@@ -33064,4 +33095,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return 0;
}
+
+ /**
+ * You can set the preferred frame rate for a View using a positive number
+ * or by specifying the preferred frame rate category using constants, including
+ * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+ * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+ * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
+ * so use this method carefully. It's important to note that the preference is valid as
+ * long as the View is invalidated.
+ *
+ * @param frameRate the preferred frame rate of the view.
+ */
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setRequestedFrameRate(float frameRate) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mPreferredFrameRate = frameRate;
+ }
+ }
+
+ /**
+ * Get the current preferred frame rate of the View.
+ * The value could be negative when preferred frame rate category is set
+ * instead of perferred frame rate.
+ * The frame rate category includes
+ * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+ * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+ * Note that the frame rate value is valid as long as the View is invalidated.
+ *
+ * @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
+ * or value passed to {@link #setRequestedFrameRate(float)}.
+ */
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public float getRequestedFrameRate() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return mPreferredFrameRate;
+ }
+ return 0;
+ }
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ec961674db60..fb2b8b9c280c 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -88,6 +88,13 @@ public class ViewConfiguration {
private static final int DEFAULT_MULTI_PRESS_TIMEOUT = 300;
/**
+ * Defines the default duration in milliseconds between a key being pressed and its first key
+ * repeat event being generated. Historically, Android used the long press timeout as the
+ * key repeat timeout, so its default value is set to long press timeout's default.
+ */
+ private static final int DEFAULT_KEY_REPEAT_TIMEOUT_MS = DEFAULT_LONG_PRESS_TIMEOUT;
+
+ /**
* Defines the default duration between successive key repeats in milliseconds.
*/
private static final int DEFAULT_KEY_REPEAT_DELAY_MS = 50;
@@ -719,11 +726,8 @@ public class ViewConfiguration {
* @return the time before the first key repeat in milliseconds.
*/
public static int getKeyRepeatTimeout() {
- // Before the key repeat timeout was introduced, some users relied on changing
- // LONG_PRESS_TIMEOUT settings to also change the key repeat timeout. To support
- // this backward compatibility, we'll use the long press timeout as default value.
return AppGlobals.getIntCoreSetting(Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
- getLongPressTimeout());
+ DEFAULT_KEY_REPEAT_TIMEOUT_MS);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d97dfb0f9c94..7b456007e4ae 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -81,6 +81,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -442,9 +444,6 @@ public final class ViewRootImpl implements ViewParent,
*/
private boolean mForceNextConfigUpdate;
- private boolean mUseBLASTAdapter;
- private boolean mForceDisableBLAST;
-
/** lazily-initialized in getAudioManager() */
private boolean mFastScrollSoundEffectsEnabled = false;
@@ -982,6 +981,9 @@ public final class ViewRootImpl implements ViewParent,
// The preferred frame rate of the view that is mainly used for
// touch boosting, view velocity handling, and TextureView.
private float mPreferredFrameRate = 0;
+ // The last preferred frame rate of the view that is mainly used to
+ // track the difference between the current preferred frame rate and the previous value.
+ private float mLastPreferredFrameRate = 0;
// Used to check if there were any view invalidations in
// the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
private boolean mHasInvalidation = false;
@@ -1287,8 +1289,6 @@ public final class ViewRootImpl implements ViewParent,
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
- mWindowAttributes.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs = mWindowAttributes;
setTag();
@@ -1499,9 +1499,6 @@ public final class ViewRootImpl implements ViewParent,
Slog.i(mTag, "(" + mBasePackageName + ") Initial DisplayState: "
+ mAttachInfo.mDisplayState, new Throwable());
}
- if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
- mUseBLASTAdapter = true;
- }
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
@@ -1898,8 +1895,7 @@ public final class ViewRootImpl implements ViewParent,
mWindowAttributes.insetsFlags.appearance = appearance;
mWindowAttributes.insetsFlags.behavior = behavior;
mWindowAttributes.privateFlags |= compatibleWindowFlag
- | appearanceAndBehaviorPrivateFlags
- | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+ | appearanceAndBehaviorPrivateFlags;
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
@@ -3991,10 +3987,12 @@ public final class ViewRootImpl implements ViewParent,
}
// For the variable refresh rate project.
+ // We set the preferred frame rate and frame rate category at the end of performTraversals
+ // when the values are applicable.
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
- mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ mPreferredFrameRate = 0;
}
private void createSyncIfNeeded() {
@@ -5881,7 +5879,7 @@ public final class ViewRootImpl implements ViewParent,
mInputQueue = null;
}
try {
- mWindowSession.remove(mWindow);
+ mWindowSession.remove(mWindow.asBinder());
} catch (RemoteException e) {
}
// Dispose receiver would dispose client InputChannel, too. That could send out a socket
@@ -7370,7 +7368,9 @@ public final class ViewRootImpl implements ViewParent,
*/
if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL)) {
- sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
+ mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
+ FRAME_RATE_TOUCH_BOOST_TIME);
}
return handled ? FINISH_HANDLED : FORWARD;
}
@@ -8683,11 +8683,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mSurfaceControl.isValid()) {
- if (!useBLAST()) {
- mSurface.copyFrom(mSurfaceControl);
- } else {
- updateBlastSurfaceIfNeeded();
- }
+ updateBlastSurfaceIfNeeded();
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl, mBlastBufferQueue);
}
@@ -11524,7 +11520,7 @@ public final class ViewRootImpl implements ViewParent,
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setBlurRegions(surfaceControl, regionCopy);
- if (useBLAST() && mBlastBufferQueue != null) {
+ if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber);
}
}
@@ -11541,18 +11537,6 @@ public final class ViewRootImpl implements ViewParent,
mUnbufferedInputSource = mView.mUnbufferedInputSource;
}
- /**
- * Force disabling use of the BLAST adapter regardless of the system
- * flag. Needs to be called before addView.
- */
- void forceDisableBLAST() {
- mForceDisableBLAST = true;
- }
-
- boolean useBLAST() {
- return mUseBLASTAdapter && !mForceDisableBLAST;
- }
-
int getSurfaceSequenceId() {
return mSurfaceSequenceId;
}
@@ -11996,8 +11980,11 @@ public final class ViewRootImpl implements ViewParent,
? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
try {
- mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
- frameRateCategory, false).apply();
+ if (mLastPreferredFrameRateCategory != frameRateCategory) {
+ mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
+ frameRateCategory, false).applyAsyncUnsafe();
+ mLastPreferredFrameRateCategory = frameRateCategory;
+ }
} catch (Exception e) {
Log.e(mTag, "Unable to set frame rate category", e);
}
@@ -12017,8 +12004,11 @@ public final class ViewRootImpl implements ViewParent,
}
try {
- mFrameRateTransaction.setFrameRate(mSurfaceControl,
- preferredFrameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).apply();
+ if (mLastPreferredFrameRate != preferredFrameRate) {
+ mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
+ Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
+ mLastPreferredFrameRate = preferredFrameRate;
+ }
} catch (Exception e) {
Log.e(mTag, "Unable to set frame rate", e);
}
@@ -12045,7 +12035,8 @@ public final class ViewRootImpl implements ViewParent,
|| motionEventAction == MotionEvent.ACTION_MOVE
|| motionEventAction == MotionEvent.ACTION_UP;
boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
- || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
+ || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION
+ || windowType == TYPE_NOTIFICATION_SHADE || windowType == TYPE_STATUS_BAR;
// use toolkitSetFrameRate flag to gate the change
return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue;
}
@@ -12062,6 +12053,35 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Allow Views to vote for the preferred frame rate
+ * 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.
+ *
+ * @param frameRate the preferred frame rate of a View
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void votePreferredFrameRate(float frameRate) {
+ 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;
+ }
+
+ mHasInvalidation = true;
+ }
+
+ /**
* Get the value of mPreferredFrameRateCategory
*/
@VisibleForTesting
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c73514250e3b..046ea77f196d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -651,6 +651,7 @@ public interface WindowManager extends ViewManager {
REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY,
REMOVE_CONTENT_MODE_DESTROY,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface RemoveContentMode {}
/**
@@ -685,6 +686,7 @@ public interface WindowManager extends ViewManager {
DISPLAY_IME_POLICY_FALLBACK_DISPLAY,
DISPLAY_IME_POLICY_HIDE,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface DisplayImePolicy {}
/**
@@ -3249,13 +3251,6 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 1 << 24;
/**
- * Flag to request creation of a BLAST (Buffer as LayerState) Layer.
- * If not specified the client will receive a BufferQueue layer.
- * @hide
- */
- public static final int PRIVATE_FLAG_USE_BLAST = 1 << 25;
-
- /**
* Flag to indicate that the window is controlling the appearance of system bars. So we
* don't need to adjust it by reading its system UI flags for compatibility.
* @hide
@@ -3306,9 +3301,8 @@ public interface WindowManager extends ViewManager {
/**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
- * @hide
+ * @removed mistakenly exposed as system-api previously
*/
- @SystemApi
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
@@ -3340,7 +3334,6 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
PRIVATE_FLAG_NOT_MAGNIFIABLE,
PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
- PRIVATE_FLAG_USE_BLAST,
PRIVATE_FLAG_APPEARANCE_CONTROLLED,
PRIVATE_FLAG_BEHAVIOR_CONTROLLED,
PRIVATE_FLAG_FIT_INSETS_CONTROLLED,
@@ -3349,6 +3342,7 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface PrivateFlags {}
/**
@@ -3438,10 +3432,6 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
name = "COLOR_SPACE_AGNOSTIC"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_USE_BLAST,
- equals = PRIVATE_FLAG_USE_BLAST,
- name = "USE_BLAST"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
equals = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
name = "APPEARANCE_CONTROLLED"),
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d20d95d50d61..214f1ec3d1ec 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -60,8 +60,6 @@ import java.util.function.IntConsumer;
public final class WindowManagerGlobal {
private static final String TAG = "WindowManager";
- private static boolean sUseBLASTAdapter = false;
-
/**
* This is the first time the window is being drawn,
* so the client must call drawingFinished() when done
@@ -99,7 +97,6 @@ public final class WindowManagerGlobal {
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
- public static final int ADD_FLAG_USE_BLAST = 0x8;
/**
* Like {@link #RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS}, but as a "hint" when adding the
@@ -176,7 +173,6 @@ public final class WindowManagerGlobal {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
- sUseBLASTAdapter = sWindowManagerService.useBLAST();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -218,13 +214,6 @@ public final class WindowManagerGlobal {
}
}
- /**
- * Whether or not to use BLAST for ViewRootImpl
- */
- public static boolean useBLAST() {
- return sUseBLASTAdapter;
- }
-
@UnsupportedAppUsage
public String[] getViewRootNames() {
synchronized (mLock) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 652fe2445ddc..d817e6f51f55 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -228,14 +228,14 @@ public class WindowlessWindowManager implements IWindowSession {
if (mRealWm instanceof IWindowSession.Stub) {
mRealWm.grantInputChannel(displayId,
new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
- window, mHostInputToken, attrs.flags, attrs.privateFlags,
+ window.asBinder(), mHostInputToken, attrs.flags, attrs.privateFlags,
attrs.inputFeatures, attrs.type,
attrs.token, state.mInputTransferToken, attrs.getTitle().toString(),
outInputChannel);
} else {
- mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
- attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
- state.mInputTransferToken, attrs.getTitle().toString(),
+ mRealWm.grantInputChannel(displayId, sc, window.asBinder(), mHostInputToken,
+ attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type,
+ attrs.token, state.mInputTransferToken, attrs.getTitle().toString(),
outInputChannel);
}
state.mInputChannelToken =
@@ -245,8 +245,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
- final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
- WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+ final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
sendLayoutParamsToParent();
// Include whether the window is in touch mode.
@@ -277,11 +276,11 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public void remove(android.view.IWindow window) throws RemoteException {
- mRealWm.remove(window);
+ public void remove(IBinder clientToken) throws RemoteException {
+ mRealWm.remove(clientToken);
State state;
synchronized (this) {
- state = mStateForWindow.remove(window.asBinder());
+ state = mStateForWindow.remove(clientToken);
}
if (state == null) {
throw new IllegalArgumentException(
@@ -593,7 +592,7 @@ public class WindowlessWindowManager implements IWindowSession {
List<Rect> unrestrictedRects) {}
@Override
- public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
+ public void grantInputChannel(int displayId, SurfaceControl surface, IBinder clientToken,
IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type,
IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
InputChannel outInputChannel) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index b220c4d0c763..07ae1345a688 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -17,6 +17,7 @@
package android.view.accessibility;
import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -25,6 +26,7 @@ import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2042,6 +2044,9 @@ public final class AccessibilityManager {
* @return {@code true} if flash notification works properly.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API)
+ @TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean startFlashNotificationSequence(@NonNull Context context,
@FlashNotificationReason int reason) {
final IAccessibilityManager service;
@@ -2071,6 +2076,9 @@ public final class AccessibilityManager {
* @return {@code true} if flash notification stops properly.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API)
+ @TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean stopFlashNotificationSequence(@NonNull Context context) {
final IAccessibilityManager service;
synchronized (mLock) {
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ab9566e1ece0..950fa4b1d711 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -17,6 +17,20 @@ flag {
}
flag {
+ name: "deduplicate_accessibility_warning_dialog"
+ namespace: "accessibility"
+ description: "Removes duplicate definition of the accessibility warning dialog."
+ bug: "303511250"
+}
+
+flag {
+ namespace: "accessibility"
+ name: "flash_notification_system_api"
+ description: "Makes flash notification APIs as system APIs for calling from mainline module"
+ bug: "282821643"
+}
+
+flag {
namespace: "accessibility"
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
index c4d43bcfdb24..eb2a101e8ee6 100644
--- a/core/java/android/view/inputmethod/HandwritingGesture.java
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -86,6 +86,7 @@ public abstract class HandwritingGesture {
* Granular level on which text should be operated.
*/
@IntDef({GRANULARITY_CHARACTER, GRANULARITY_WORD})
+ @Retention(RetentionPolicy.SOURCE)
@interface Granularity {}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index eeab005771f5..589b7a3eeda4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1176,24 +1176,33 @@ public final class InputMethodManager {
mActive = interactive;
mFullscreenMode = fullscreen;
if (interactive) {
+ // Find the next view focus to start the input connection when the
+ // device was interactive.
final View rootView =
mCurRootView != null ? mCurRootView.getView() : null;
if (rootView == null) {
+ // No window focused or view was removed, ignore request.
return;
}
- // Find the next view focus to start the input connection when the
- // device was interactive.
final ViewRootImpl currentViewRootImpl = mCurRootView;
+ // Post this on UI thread as required for view focus code.
rootView.post(() -> {
synchronized (mH) {
if (mCurRootView != currentViewRootImpl) {
+ // Focused window changed since posting, ignore request.
return;
}
}
- final View focusedView = currentViewRootImpl.getView().findFocus();
+ final View curRootView = currentViewRootImpl.getView();
+ if (curRootView == null) {
+ // View was removed, ignore request.
+ return;
+ }
+ final View focusedView = curRootView.findFocus();
onViewFocusChangedInternal(focusedView, focusedView != null);
});
} else {
+ // Finish input connection when device becomes non-interactive.
finishInputLocked();
if (isImeSessionAvailableLocked()) {
mCurBindState.mImeSession.finishInput();
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 1e8718ce42c0..7486362195d0 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -22,4 +22,12 @@ flag {
description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
bug: "301713309"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ name: "concurrent_input_methods"
+ namespace: "input_method"
+ description: "Feature flag for concurrent multi-session IME"
+ bug: "284527000"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index e44f43609256..4816f35e6a07 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -27,6 +27,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Information to be sent to SysUI about a back event.
*
@@ -85,6 +88,7 @@ public final class BackNavigationInfo implements Parcelable {
TYPE_CROSS_TASK,
TYPE_CALLBACK
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface BackTargetType {
}
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 0ce076b6eb96..1bd921b339f6 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -22,6 +22,8 @@ import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* The window frame container class used by client side for layout.
* @hide
@@ -101,6 +103,29 @@ public class ClientWindowFrames implements Parcelable {
}
@Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ClientWindowFrames other = (ClientWindowFrames) o;
+ return frame.equals(other.frame)
+ && displayFrame.equals(other.displayFrame)
+ && parentFrame.equals(other.parentFrame)
+ && Objects.equals(attachedFrame, other.attachedFrame)
+ && isParentFrameClippedByDisplayCutout == other.isParentFrameClippedByDisplayCutout
+ && compatScale == other.compatScale;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(frame, displayFrame, parentFrame, attachedFrame,
+ isParentFrameClippedByDisplayCutout, compatScale);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 95e9e861bea2..befb0023ebe6 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -528,14 +528,12 @@ public class ScreenCapture {
private final IBinder mDisplayToken;
private final int mWidth;
private final int mHeight;
- private final boolean mUseIdentityTransform;
private DisplayCaptureArgs(Builder builder) {
super(builder);
mDisplayToken = builder.mDisplayToken;
mWidth = builder.mWidth;
mHeight = builder.mHeight;
- mUseIdentityTransform = builder.mUseIdentityTransform;
}
/**
@@ -545,7 +543,6 @@ public class ScreenCapture {
private IBinder mDisplayToken;
private int mWidth;
private int mHeight;
- private boolean mUseIdentityTransform;
/**
* Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
@@ -586,17 +583,6 @@ public class ScreenCapture {
return this;
}
- /**
- * Replace the rotation transform of the display with the identity transformation while
- * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
- * orientation. Set this value to false if the screenshot should be taken in the
- * current screen orientation.
- */
- public Builder setUseIdentityTransform(boolean useIdentityTransform) {
- mUseIdentityTransform = useIdentityTransform;
- return this;
- }
-
@Override
Builder getThis() {
return this;
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 758582615a46..cc875ad7ad1b 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -37,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
@@ -426,7 +425,7 @@ public class SnapshotDrawerUtils {
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
- | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
+ | PRIVATE_FLAG_TRUSTED_OVERLAY;
layoutParams.token = token;
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index f1c0d8dee525..b6c04d94b476 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -33,6 +33,8 @@ import android.util.Log;
import android.util.Singleton;
import android.util.Slog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -65,6 +67,7 @@ public interface SplashScreen {
SPLASH_SCREEN_STYLE_SOLID_COLOR,
SPLASH_SCREEN_STYLE_ICON
})
+ @Retention(RetentionPolicy.SOURCE)
@interface SplashScreenStyle {}
/**
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
new file mode 100644
index 000000000000..9fe30df13036
--- /dev/null
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -0,0 +1,17 @@
+package: "com.android.window.flags"
+
+flag {
+ name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
+ namespace: "large_screen_experiences_app_compat"
+ description: "When necessary, configuration decoupled from status bar and display cutout"
+ bug: "291870756"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "movable_cutout_configuration"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Make it possible to move cutout across edges through device config"
+ bug: "302387383"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 68eddff19922..11bd22f7d6ff 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -32,3 +32,11 @@ flag {
description: "Enable public API for Window Surfaces"
bug: "287076178"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "remove_capture_display"
+ description: "Remove uses of ScreenCapture#captureDisplay"
+ is_fixed_read_only: true
+ bug: "293445881"
+} \ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 0ad6c99422b8..b600b22751ff 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -35,4 +35,11 @@ flag {
name: "fullscreen_dim_flag"
description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
bug: "253533308"
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "activity_embedding_interactive_divider_flag"
+ description: "Whether the interactive divider feature is enabled"
+ bug: "293654166"
} \ No newline at end of file
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java
index 64974090938f..2b6913ca5e5a 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java
@@ -34,6 +34,8 @@ import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuM
*/
class AccessibilityServiceTarget extends AccessibilityTarget {
+ private final AccessibilityServiceInfo mAccessibilityServiceInfo;
+
AccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
@AccessibilityFragmentType int fragmentType,
@NonNull AccessibilityServiceInfo serviceInfo) {
@@ -47,6 +49,7 @@ class AccessibilityServiceTarget extends AccessibilityTarget {
serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()),
serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()),
convertToKey(convertToUserType(shortcutType)));
+ mAccessibilityServiceInfo = serviceInfo;
}
@Override
@@ -64,4 +67,8 @@ class AccessibilityServiceTarget extends AccessibilityTarget {
holder.mLabelView.setEnabled(enabled);
holder.mStatusView.setEnabled(enabled);
}
+
+ public AccessibilityServiceInfo getAccessibilityServiceInfo() {
+ return mAccessibilityServiceInfo;
+ }
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java
new file mode 100644
index 000000000000..0f8ced27e8c1
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java
@@ -0,0 +1,139 @@
+/*
+ * 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.accessibility.dialog;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.BidiFormatter;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Utility class for creating the dialog that asks the user for explicit permission
+ * before an accessibility service is enabled.
+ */
+public class AccessibilityServiceWarning {
+
+ /**
+ * Returns an {@link AlertDialog} to be shown to confirm that the user
+ * wants to enable an {@link android.accessibilityservice.AccessibilityService}.
+ */
+ public static AlertDialog createAccessibilityServiceWarningDialog(@NonNull Context context,
+ @NonNull AccessibilityServiceInfo info,
+ @NonNull View.OnClickListener allowListener,
+ @NonNull View.OnClickListener denyListener,
+ @NonNull View.OnClickListener uninstallListener) {
+ final AlertDialog ad = new AlertDialog.Builder(context)
+ .setView(createAccessibilityServiceWarningDialogContentView(
+ context, info, allowListener, denyListener, uninstallListener))
+ .setCancelable(true)
+ .create();
+ Window window = ad.getWindow();
+ WindowManager.LayoutParams params = window.getAttributes();
+ params.privateFlags |= SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ window.setAttributes(params);
+ return ad;
+ }
+
+ @VisibleForTesting
+ public static View createAccessibilityServiceWarningDialogContentView(Context context,
+ AccessibilityServiceInfo info,
+ View.OnClickListener allowListener,
+ View.OnClickListener denyListener,
+ View.OnClickListener uninstallListener) {
+ final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
+ final View content = inflater.inflate(R.layout.accessibility_service_warning, null);
+
+ final Drawable icon;
+ if (info.getResolveInfo().getIconResource() == 0) {
+ icon = context.getDrawable(R.drawable.ic_accessibility_generic);
+ } else {
+ icon = info.getResolveInfo().loadIcon(context.getPackageManager());
+ }
+ final ImageView permissionDialogIcon = content.findViewById(
+ R.id.accessibility_permissionDialog_icon);
+ permissionDialogIcon.setImageDrawable(icon);
+
+ final TextView permissionDialogTitle = content.findViewById(
+ R.id.accessibility_permissionDialog_title);
+ permissionDialogTitle.setText(context.getString(R.string.accessibility_enable_service_title,
+ getServiceName(context, info)));
+
+ final Button permissionAllowButton = content.findViewById(
+ R.id.accessibility_permission_enable_allow_button);
+ final Button permissionDenyButton = content.findViewById(
+ R.id.accessibility_permission_enable_deny_button);
+ permissionAllowButton.setOnClickListener(allowListener);
+ permissionAllowButton.setOnTouchListener(getTouchConsumingListener());
+ permissionDenyButton.setOnClickListener(denyListener);
+
+ final Button uninstallButton = content.findViewById(
+ R.id.accessibility_permission_enable_uninstall_button);
+ // Show an uninstall button to help users quickly remove non-preinstalled apps.
+ if (!info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+ uninstallButton.setVisibility(View.VISIBLE);
+ uninstallButton.setOnClickListener(uninstallListener);
+ }
+ return content;
+ }
+
+ @VisibleForTesting
+ @SuppressLint("ClickableViewAccessibility") // Touches are intentionally consumed
+ public static View.OnTouchListener getTouchConsumingListener() {
+ return (view, event) -> {
+ // Filter obscured touches by consuming them.
+ if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
+ || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ Toast.makeText(view.getContext(),
+ R.string.accessibility_dialog_touch_filtered_warning,
+ Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ return false;
+ };
+ }
+
+ // Get the service name and bidi wrap it to protect from bidi side effects.
+ private static CharSequence getServiceName(Context context, AccessibilityServiceInfo info) {
+ final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
+ final CharSequence label =
+ info.getResolveInfo().loadLabel(context.getPackageManager());
+ return BidiFormatter.getInstance(locale).unicodeWrap(label);
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 987c14c6ab51..d4eccd458e35 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -28,6 +28,7 @@ import static com.android.internal.accessibility.util.AccessibilityUtils.isUserS
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -56,7 +57,7 @@ public class AccessibilityShortcutChooserActivity extends Activity {
"accessibility_shortcut_menu_mode";
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
private AlertDialog mMenuDialog;
- private AlertDialog mPermissionDialog;
+ private Dialog mPermissionDialog;
private ShortcutTargetAdapter mTargetAdapter;
@Override
@@ -123,7 +124,7 @@ public class AccessibilityShortcutChooserActivity extends Activity {
if (target instanceof AccessibilityServiceTarget) {
showPermissionDialogIfNeeded(this, (AccessibilityServiceTarget) target,
- mTargetAdapter);
+ position, mTargetAdapter);
return;
}
}
@@ -149,20 +150,43 @@ public class AccessibilityShortcutChooserActivity extends Activity {
}
private void showPermissionDialogIfNeeded(Context context,
- AccessibilityServiceTarget serviceTarget, ShortcutTargetAdapter targetAdapter) {
+ AccessibilityServiceTarget serviceTarget, int position,
+ ShortcutTargetAdapter targetAdapter) {
if (mPermissionDialog != null) {
return;
}
- mPermissionDialog = new AlertDialog.Builder(context)
- .setView(createEnableDialogContentView(context, serviceTarget,
- v -> {
- mPermissionDialog.dismiss();
- targetAdapter.notifyDataSetChanged();
- },
- v -> mPermissionDialog.dismiss()))
- .setOnDismissListener(dialog -> mPermissionDialog = null)
- .create();
+ if (Flags.deduplicateAccessibilityWarningDialog()) {
+ mPermissionDialog = AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(context,
+ serviceTarget.getAccessibilityServiceInfo(),
+ v -> {
+ serviceTarget.onCheckedChanged(true);
+ targetAdapter.notifyDataSetChanged();
+ mPermissionDialog.dismiss();
+ }, v -> {
+ serviceTarget.onCheckedChanged(false);
+ mPermissionDialog.dismiss();
+ },
+ v -> {
+ mTargets.remove(position);
+ context.getPackageManager().getPackageInstaller().uninstall(
+ serviceTarget.getComponentName().getPackageName(), null);
+ targetAdapter.notifyDataSetChanged();
+ mPermissionDialog.dismiss();
+ });
+ mPermissionDialog.setOnDismissListener(dialog -> mPermissionDialog = null);
+ } else {
+ mPermissionDialog = new AlertDialog.Builder(context)
+ .setView(createEnableDialogContentView(context, serviceTarget,
+ v -> {
+ mPermissionDialog.dismiss();
+ targetAdapter.notifyDataSetChanged();
+ },
+ v -> mPermissionDialog.dismiss()))
+ .setOnDismissListener(dialog -> mPermissionDialog = null)
+ .create();
+ }
mPermissionDialog.show();
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 0f85075a0d6c..51a5ddfa8dd6 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -296,6 +296,10 @@ public final class AccessibilityTargetHelper {
}
}
+ /**
+ * @deprecated Use {@link AccessibilityServiceWarning}.
+ */
+ @Deprecated
static View createEnableDialogContentView(Context context,
AccessibilityServiceTarget target, View.OnClickListener allowListener,
View.OnClickListener denyListener) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 755113b22088..0dbdb36977f4 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -690,18 +690,6 @@ public final class ProcessState {
}
}
- public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
- ensureNotDead();
- mCommonProcess.addCachedKill(1, pss, pss, pss);
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
- }
- }
-
public ProcessState pullFixedProc(String pkgName) {
if (mMultiPackage) {
// The array map is still pointing to a common process state
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index df6c1538fc6d..e3bb1fe8b736 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -66,10 +66,6 @@ public class SystemUiSystemPropertiesFlags {
public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI =
releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi");
- /** Gating the redaction of OTP notifications on the lockscreen */
- public static final Flag OTP_REDACTION =
- devFlag("persist.sysui.notification.otp_redaction");
-
/** Gating the logging of DND state change events. */
public static final Flag LOG_DND_STATE_EVENTS =
releasedFlag("persist.sysui.notification.log_dnd_state_events");
diff --git a/core/java/com/android/internal/jank/DisplayRefreshRate.java b/core/java/com/android/internal/jank/DisplayRefreshRate.java
new file mode 100644
index 000000000000..354c4132dcd1
--- /dev/null
+++ b/core/java/com/android/internal/jank/DisplayRefreshRate.java
@@ -0,0 +1,86 @@
+/*
+ * 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.jank;
+
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__UNKNOWN_REFRESH_RATE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__VARIABLE_REFRESH_RATE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_30_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_60_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_90_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_120_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_240_HZ;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Display refresh rate related functionality.
+ * @hide
+ */
+public class DisplayRefreshRate {
+ public static final int UNKNOWN_REFRESH_RATE =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__UNKNOWN_REFRESH_RATE;
+ public static final int VARIABLE_REFRESH_RATE =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__VARIABLE_REFRESH_RATE;
+ public static final int REFRESH_RATE_30_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_30_HZ;
+ public static final int REFRESH_RATE_60_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_60_HZ;
+ public static final int REFRESH_RATE_90_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_90_HZ;
+ public static final int REFRESH_RATE_120_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_120_HZ;
+ public static final int REFRESH_RATE_240_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_240_HZ;
+
+ /** @hide */
+ @IntDef({
+ UNKNOWN_REFRESH_RATE,
+ VARIABLE_REFRESH_RATE,
+ REFRESH_RATE_30_HZ,
+ REFRESH_RATE_60_HZ,
+ REFRESH_RATE_90_HZ,
+ REFRESH_RATE_120_HZ,
+ REFRESH_RATE_240_HZ,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RefreshRate {
+ }
+
+ private DisplayRefreshRate() {
+ }
+
+ /**
+ * Computes the display refresh rate based off the frame interval.
+ */
+ @RefreshRate
+ public static int getRefreshRate(long frameIntervalNs) {
+ long rate = Math.round(1e9 / frameIntervalNs);
+ if (rate < 50) {
+ return REFRESH_RATE_30_HZ;
+ } else if (rate < 80) {
+ return REFRESH_RATE_60_HZ;
+ } else if (rate < 110) {
+ return REFRESH_RATE_90_HZ;
+ } else if (rate < 180) {
+ return REFRESH_RATE_120_HZ;
+ } else {
+ return REFRESH_RATE_240_HZ;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 506f19f7719a..c83452d88290 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,6 +24,8 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADL
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
+import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
+import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT;
@@ -49,6 +51,7 @@ import android.view.ViewRootImpl;
import android.view.WindowCallbacks;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.DisplayRefreshRate.RefreshRate;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
@@ -132,26 +135,30 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
boolean hwuiCallbackFired;
boolean surfaceControlCallbackFired;
@JankType int jankType;
+ @RefreshRate int refreshRate;
static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos,
boolean isFirstFrame) {
- return new JankInfo(frameVsyncId, true, false, JANK_NONE, totalDurationNanos,
- isFirstFrame);
+ return new JankInfo(frameVsyncId, true, false, JANK_NONE, UNKNOWN_REFRESH_RATE,
+ totalDurationNanos, isFirstFrame);
}
static JankInfo createFromSurfaceControlCallback(long frameVsyncId,
- @JankType int jankType) {
- return new JankInfo(frameVsyncId, false, true, jankType, 0, false /* isFirstFrame */);
+ @JankType int jankType, @RefreshRate int refreshRate) {
+ return new JankInfo(
+ frameVsyncId, false, true, jankType, refreshRate, 0, false /* isFirstFrame */);
}
private JankInfo(long frameVsyncId, boolean hwuiCallbackFired,
boolean surfaceControlCallbackFired, @JankType int jankType,
+ @RefreshRate int refreshRate,
long totalDurationNanos, boolean isFirstFrame) {
this.frameVsyncId = frameVsyncId;
this.hwuiCallbackFired = hwuiCallbackFired;
this.surfaceControlCallbackFired = surfaceControlCallbackFired;
- this.totalDurationNanos = totalDurationNanos;
this.jankType = jankType;
+ this.refreshRate = refreshRate;
+ this.totalDurationNanos = totalDurationNanos;
this.isFirstFrame = isFirstFrame;
}
@@ -468,14 +475,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
if (!isInRange(jankStat.frameVsyncId)) {
continue;
}
+ int refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
JankInfo info = findJankInfo(jankStat.frameVsyncId);
if (info != null) {
info.surfaceControlCallbackFired = true;
info.jankType = jankStat.jankType;
+ info.refreshRate = refreshRate;
} else {
mJankInfos.put((int) jankStat.frameVsyncId,
JankInfo.createFromSurfaceControlCallback(
- jankStat.frameVsyncId, jankStat.jankType));
+ jankStat.frameVsyncId, jankStat.jankType, refreshRate));
}
}
processJankInfos();
@@ -592,6 +601,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
int missedSfFramesCount = 0;
int maxSuccessiveMissedFramesCount = 0;
int successiveMissedFramesCount = 0;
+ @RefreshRate int refreshRate = UNKNOWN_REFRESH_RATE;
for (int i = 0; i < mJankInfos.size(); i++) {
JankInfo info = mJankInfos.valueAt(i);
@@ -627,6 +637,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
maxSuccessiveMissedFramesCount, successiveMissedFramesCount);
successiveMissedFramesCount = 0;
}
+ if (info.refreshRate != UNKNOWN_REFRESH_RATE && info.refreshRate != refreshRate) {
+ refreshRate = (refreshRate == UNKNOWN_REFRESH_RATE)
+ ? info.refreshRate : VARIABLE_REFRESH_RATE;
+ }
// TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now.
if (!mSurfaceOnly && !info.hwuiCallbackFired) {
@@ -669,6 +683,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mStatsLog.write(
FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
mDisplayId,
+ refreshRate,
mSession.getStatsdInteractionType(),
totalFramesCount,
missedFramesCount,
@@ -866,10 +881,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
/** {@see FrameworkStatsLog#write) */
- public void write(int code, int displayId,
+ public void write(int code, int displayId, @RefreshRate int refreshRate,
int arg1, long arg2, long arg3, long arg4, long arg5, long arg6, long arg7) {
FrameworkStatsLog.write(code, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- mDisplayResolutionTracker.getResolution(displayId));
+ mDisplayResolutionTracker.getResolution(displayId), refreshRate);
}
}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 0947ec178c77..f62094d231ef 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -618,4 +618,14 @@ public final class VpnProfile implements Cloneable, Parcelable {
public int describeContents() {
return 0;
}
+
+ @Override
+ public VpnProfile clone() {
+ try {
+ return (VpnProfile) super.clone();
+ } catch (CloneNotSupportedException e) {
+ Log.wtf(TAG, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 70514c30d90d..01c91bae72cd 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -337,6 +337,12 @@ public class ProcessCpuTracker {
@UnsupportedAppUsage
public void update() {
+ synchronized (this) {
+ updateLocked();
+ }
+ }
+
+ private void updateLocked() {
if (DEBUG) Slog.v(TAG, "Update: " + this);
final long nowUptime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
index 1826f7a38e26..b0f35784fbbd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,19 +26,6 @@ import java.util.Set;
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface ParsedActivity extends ParsedMainComponent {
- /**
- * Generate activity object that forwards user to App Details page automatically.
- * This activity should be invisible to user and user should not know or see it.
- * @hide
- */
- @NonNull
- static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
- int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
- // Proxy method since ParsedActivityImpl is supposed to be package visibility
- return ParsedActivityImpl.makeAppDetailsActivity(packageName, processName, uiOptions,
- taskAffinity, hardwareAccelerated);
- }
-
int getColorMode();
int getConfigChanges();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java
index cf478b1da2e4..adb5f4f6d2a2 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java
index 1a5d110ca8ff..5b623cdbaebb 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.StringRes;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
index 5b6ecbac1359..319ed7f23d47 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java
index c325d8dab296..76c500827a32 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java
index a7f7b0086c00..fee0017c4a31 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
index b926d5345149..291ed0c44ddd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermission.java
index dc5347a41fcf..813d14dc61b3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermission.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java
index 64f4fbd440a2..a5ba51307f01 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
/** @hide */
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
index 608d08eeb984..e5247f99f398 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java b/core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
index c66a5c1b6c92..ba5470f12017 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/core/java/com/android/internal/pm/pkg/component/ParsedService.java
index 5fc251ccab47..e3611021442e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java
index e17d1c4f5184..984d50ddfc70 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 3977666627b7..fc60f065a965 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -90,7 +90,7 @@ interface IStatusBarService
void onNotificationSettingsViewed(String key);
void onNotificationBubbleChanged(String key, boolean isBubble, int flags);
void onBubbleMetadataFlagChanged(String key, int flags);
- void hideCurrentInputMethodForBubbles();
+ void hideCurrentInputMethodForBubbles(int displayId);
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
oneway void clearInlineReplyUriPermissions(String key);
void onNotificationFeedbackReceived(String key, in Bundle feedback);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 686e1fc2c34e..9d0be4bf8ee6 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -40,6 +40,7 @@ import java.util.function.IntFunction;
/**
* Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrayUtils {
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
@@ -48,35 +49,43 @@ public class ArrayUtils {
private ArrayUtils() { /* cannot be instantiated */ }
+ @android.ravenwood.annotation.RavenwoodReplace
public static byte[] newUnpaddedByteArray(int minLen) {
return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static char[] newUnpaddedCharArray(int minLen) {
return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int[] newUnpaddedIntArray(int minLen) {
return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static boolean[] newUnpaddedBooleanArray(int minLen) {
return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static long[] newUnpaddedLongArray(int minLen) {
return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static float[] newUnpaddedFloatArray(int minLen) {
return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static Object[] newUnpaddedObjectArray(int minLen) {
return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SuppressWarnings("unchecked")
public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
diff --git a/core/java/com/android/internal/util/ArtFastDataInput.java b/core/java/com/android/internal/util/ArtFastDataInput.java
index 3e8916caead9..768ea82e0c71 100644
--- a/core/java/com/android/internal/util/ArtFastDataInput.java
+++ b/core/java/com/android/internal/util/ArtFastDataInput.java
@@ -21,6 +21,8 @@ import android.util.CharsetUtils;
import com.android.modules.utils.FastDataInput;
+import dalvik.system.VMRuntime;
+
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
@@ -35,13 +37,14 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class ArtFastDataInput extends FastDataInput {
private static AtomicReference<ArtFastDataInput> sInCache = new AtomicReference<>();
+ private static VMRuntime sRuntime = VMRuntime.getRuntime();
private final long mBufferPtr;
public ArtFastDataInput(@NonNull InputStream in, int bufferSize) {
super(in, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
+ mBufferPtr = sRuntime.addressOf(mBuffer);
}
/**
@@ -66,6 +69,7 @@ public class ArtFastDataInput extends FastDataInput {
* Release a {@link ArtFastDataInput} to potentially be recycled. You must not
* interact with the object after releasing it.
*/
+ @Override
public void release() {
super.release();
@@ -76,6 +80,11 @@ public class ArtFastDataInput extends FastDataInput {
}
@Override
+ public byte[] newByteArray(int bufferSize) {
+ return (byte[]) sRuntime.newNonMovableArray(byte.class, bufferSize);
+ }
+
+ @Override
public String readUTF() throws IOException {
// Attempt to read directly from buffer space if there's enough room,
// otherwise fall back to chunking into place
@@ -86,9 +95,9 @@ public class ArtFastDataInput extends FastDataInput {
mBufferPos += len;
return res;
} else {
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ final byte[] tmp = (byte[]) sRuntime.newNonMovableArray(byte.class, len + 1);
readFully(tmp, 0, len);
- return CharsetUtils.fromModifiedUtf8Bytes(mRuntime.addressOf(tmp), 0, len);
+ return CharsetUtils.fromModifiedUtf8Bytes(sRuntime.addressOf(tmp), 0, len);
}
}
}
diff --git a/core/java/com/android/internal/util/ArtFastDataOutput.java b/core/java/com/android/internal/util/ArtFastDataOutput.java
index ac595b6fd151..360ddb814aa2 100644
--- a/core/java/com/android/internal/util/ArtFastDataOutput.java
+++ b/core/java/com/android/internal/util/ArtFastDataOutput.java
@@ -21,6 +21,8 @@ import android.util.CharsetUtils;
import com.android.modules.utils.FastDataOutput;
+import dalvik.system.VMRuntime;
+
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
@@ -35,13 +37,14 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class ArtFastDataOutput extends FastDataOutput {
private static AtomicReference<ArtFastDataOutput> sOutCache = new AtomicReference<>();
+ private static VMRuntime sRuntime = VMRuntime.getRuntime();
private final long mBufferPtr;
public ArtFastDataOutput(@NonNull OutputStream out, int bufferSize) {
super(out, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
+ mBufferPtr = sRuntime.addressOf(mBuffer);
}
/**
@@ -73,6 +76,11 @@ public class ArtFastDataOutput extends FastDataOutput {
}
@Override
+ public byte[] newByteArray(int bufferSize) {
+ return (byte[]) sRuntime.newNonMovableArray(byte.class, bufferSize);
+ }
+
+ @Override
public void writeUTF(String s) throws IOException {
// Attempt to write directly to buffer space if there's enough room,
// otherwise fall back to chunking into place
@@ -94,8 +102,8 @@ public class ArtFastDataOutput extends FastDataOutput {
// Negative value indicates buffer was too small and we need to
// allocate a temporary buffer for encoding
len = -len;
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
- CharsetUtils.toModifiedUtf8Bytes(s, mRuntime.addressOf(tmp), 0, tmp.length);
+ final byte[] tmp = (byte[]) sRuntime.newNonMovableArray(byte.class, len + 1);
+ CharsetUtils.toModifiedUtf8Bytes(s, sRuntime.addressOf(tmp), 0, tmp.length);
writeShort(len);
write(tmp, 0, len);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a3e0016f9174..28fd2b488426 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1936,7 +1936,8 @@ public class LockPatternUtils {
* If the user is not secured, ie doesn't have an LSKF, then decrypt the user's synthetic
* password and use it to unlock various cryptographic keys associated with the user. This
* primarily includes unlocking the user's credential-encrypted (CE) storage. It also includes
- * deriving or decrypting the vendor auth secret and sending it to the AuthSecret HAL.
+ * unlocking the user's Keystore super keys, and deriving or decrypting the vendor auth secret
+ * and sending it to the AuthSecret HAL in order to unlock Secure Element firmware updates.
* <p>
* These tasks would normally be done when the LSKF is verified. This method is where these
* tasks are done when the user doesn't have an LSKF. It's called when the user is started.
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 139b88b108c5..61e017d3443f 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
}
@Override
- public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos,
+ public void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos,
int uid) {
// default no-op
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index ea3c70f5e60b..ae23942f2500 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -59,6 +59,7 @@ static struct {
jfieldID layoutParamsType;
jfieldID dispatchingTimeoutMillis;
jfieldID frame;
+ jfieldID contentSize;
jfieldID surfaceInset;
jfieldID scaleFactor;
jfieldID touchableRegion;
@@ -73,6 +74,7 @@ static struct {
jfieldID transform;
jfieldID windowToken;
jfieldID focusTransferTarget;
+ jfieldID alpha;
} gInputWindowHandleClassInfo;
static struct {
@@ -281,6 +283,9 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn
ScopedLocalRef<jobject> rectObj(env, JNICommon::objFromRect(env, windowInfo.frame));
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.frame, rectObj.get());
+ ScopedLocalRef<jobject> sizeObj(env, JNICommon::objFromSize(env, windowInfo.contentSize));
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.contentSize, sizeObj.get());
+
env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset,
windowInfo.surfaceInset);
env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor,
@@ -321,6 +326,8 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
javaObjectForIBinder(env, windowInfo.windowToken));
+ env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
+
return inputWindowHandle;
}
@@ -393,6 +400,9 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.frame, clazz, "frame", "Landroid/graphics/Rect;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.contentSize, clazz, "contentSize",
+ "Landroid/util/Size;");
+
GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
"surfaceInset", "I");
@@ -439,6 +449,8 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl, clazz,
"touchableRegionSurfaceControl", "Ljava/lang/ref/WeakReference;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
+
jclass surfaceControlClazz;
FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b1dab85d2e27..f9d00edce3fa 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -212,14 +212,11 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
}
- size_t bytesPerSample = audio_bytes_per_sample(format);
-
if (buffSizeInBytes == 0) {
ALOGE("Error creating AudioRecord: frameCount is 0.");
return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
}
- size_t frameSize = channelCount * bytesPerSample;
- size_t frameCount = buffSizeInBytes / frameSize;
+ size_t frameCount = buffSizeInBytes / audio_bytes_per_frame(channelCount, format);
// create an uninitialized AudioRecord object
Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
@@ -574,7 +571,7 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th
if (result != NO_ERROR) {
return -1;
}
- return frameCount * channelCount * audio_bytes_per_sample(format);
+ return frameCount * audio_bytes_per_frame(channelCount, format);
}
static jboolean android_media_AudioRecord_setInputDevice(
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 18c60a793166..91dfc6023e42 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1245,7 +1245,7 @@ jint android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jin
void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz)
{
- return removeAllProcessGroups();
+ return removeAllEmptyProcessGroups();
}
static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a800e6ea60e4..db42246ca76c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1951,8 +1951,9 @@ public:
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
- jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
- gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
+ jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
+ jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameIntervalNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
env->DeleteLocalRef(jJankData);
}
@@ -2523,8 +2524,7 @@ int register_android_view_SurfaceControl(JNIEnv* env)
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");
gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
- gJankDataClassInfo.ctor =
- GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JI)V");
+ gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJ)V");
jclass onJankDataListenerClazz =
FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index bdf7eaa8aace..6e903b3ab56d 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -53,7 +53,6 @@ static struct {
jfieldID displayToken;
jfieldID width;
jfieldID height;
- jfieldID useIdentityTransform;
} gDisplayCaptureArgsClassInfo;
static struct {
@@ -194,9 +193,6 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
captureArgs.height =
env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
- captureArgs.useIdentityTransform =
- env->GetBooleanField(displayCaptureArgsObject,
- gDisplayCaptureArgsClassInfo.useIdentityTransform);
return captureArgs;
}
@@ -325,8 +321,6 @@ int register_android_window_ScreenCapture(JNIEnv* env) {
GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
gDisplayCaptureArgsClassInfo.height =
GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
- gDisplayCaptureArgsClassInfo.useIdentityTransform =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
jclass layerCaptureArgsClazz =
FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
diff --git a/core/jni/jni_common.cpp b/core/jni/jni_common.cpp
index b81c9b6eed95..dd69b16f5a96 100644
--- a/core/jni/jni_common.cpp
+++ b/core/jni/jni_common.cpp
@@ -34,6 +34,11 @@ static struct {
jfieldID top;
} gRectClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gSizeClassInfo;
+
Rect JNICommon::rectFromObj(JNIEnv* env, jobject rectObj) {
int left = env->GetIntField(rectObj, gRectClassInfo.left);
int top = env->GetIntField(rectObj, gRectClassInfo.top);
@@ -47,6 +52,10 @@ jobject JNICommon::objFromRect(JNIEnv* env, Rect rect) {
rect.right, rect.bottom);
}
+jobject JNICommon::objFromSize(JNIEnv* env, Size size) {
+ return env->NewObject(gSizeClassInfo.clazz, gSizeClassInfo.ctor, size.width, size.height);
+}
+
int register_jni_common(JNIEnv* env) {
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.clazz = MakeGlobalRefOrDie(env, rectClazz);
@@ -55,6 +64,11 @@ int register_jni_common(JNIEnv* env) {
gRectClassInfo.left = GetFieldIDOrDie(env, rectClazz, "left", "I");
gRectClassInfo.right = GetFieldIDOrDie(env, rectClazz, "right", "I");
gRectClassInfo.top = GetFieldIDOrDie(env, rectClazz, "top", "I");
+
+ jclass sizeClazz = FindClassOrDie(env, "android/util/Size");
+ gSizeClassInfo.clazz = MakeGlobalRefOrDie(env, sizeClazz);
+ gSizeClassInfo.ctor = GetMethodIDOrDie(env, sizeClazz, "<init>", "(II)V");
+
return 0;
}
diff --git a/core/jni/jni_common.h b/core/jni/jni_common.h
index d670a7d6bd59..f1c60148d6b6 100644
--- a/core/jni/jni_common.h
+++ b/core/jni/jni_common.h
@@ -14,14 +14,17 @@
* limitations under the License.
*/
#include <jni.h>
+#include <ui/Size.h>
namespace android {
class Rect;
+using ui::Size;
class JNICommon {
public:
static Rect rectFromObj(JNIEnv* env, jobject rectObj);
static jobject objFromRect(JNIEnv* env, Rect rect);
+ static jobject objFromSize(JNIEnv* env, Size size);
};
} // namespace android \ No newline at end of file
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 4e686b7ad80c..34c404520a2b 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -152,6 +152,8 @@ android_app {
"simulated_device_launcher",
],
},
+
+ generate_product_characteristics_rro: true,
}
java_genrule {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6859f1fd0886..7b075e6f4872 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -822,6 +822,7 @@
<protected-broadcast android:name="android.intent.action.PROFILE_REMOVED" />
<protected-broadcast android:name="com.android.internal.telephony.cat.SMS_SENT_ACTION" />
<protected-broadcast android:name="com.android.internal.telephony.cat.SMS_DELIVERY_ACTION" />
+ <protected-broadcast android:name="com.android.internal.telephony.data.ACTION_RETRY" />
<protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
<protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" />
<protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" />
@@ -2372,7 +2373,7 @@
them from running without explicit user action.
-->
<permission android:name="android.permission.QUARANTINE_APPS"
- android:protectionLevel="internal|verifier" />
+ android:protectionLevel="signature|verifier" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
@@ -8391,6 +8392,10 @@
android:exported="true">
</provider>
+ <meta-data
+ android:name="com.android.server.patch.25239169"
+ android:value="true" />
+
</application>
</manifest>
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 0df7c2047bc1..f24c3f59155a 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,5 +1,6 @@
adamp@google.com
asc@google.com
+austindelgado@google.com
cinek@google.com
dsandler@android.com
dsandler@google.com
@@ -8,6 +9,7 @@ hackbod@android.com
hackbod@google.com
ilyamaty@google.com
jaggies@google.com
+jbolinger@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
diff --git a/core/res/res/drawable-nodpi/ic_thermostat_notification.xml b/core/res/res/drawable-nodpi/ic_thermostat_notification.xml
new file mode 100644
index 000000000000..561c43db23a2
--- /dev/null
+++ b/core/res/res/drawable-nodpi/ic_thermostat_notification.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="16"
+ android:viewportHeight="16">
+ <group>
+ <clip-path
+ android:pathData="M0,0h16v16h-16z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:fillType="evenOdd"
+ android:pathData="M8.9,7.1V5.75H12.5V7.1H8.9ZM8.9,4.4V3.05L14.3,3.05V4.4L8.9,4.4ZM5.3,14.3C4.3,14.3 3.45,13.95 2.75,13.25C2.05,12.55 1.7,11.7 1.7,10.7C1.7,10.075 1.837,9.5 2.112,8.975C2.4,8.45 2.787,8.031 3.275,7.719L3.275,3.725C3.275,3.162 3.469,2.688 3.856,2.3C4.256,1.9 4.738,1.7 5.3,1.7C5.863,1.7 6.338,1.9 6.725,2.3C7.125,2.688 7.325,3.162 7.325,3.725V7.719C7.813,8.031 8.194,8.45 8.469,8.975C8.756,9.5 8.9,10.075 8.9,10.7C8.9,11.7 8.55,12.55 7.85,13.25C7.15,13.95 6.3,14.3 5.3,14.3ZM3.05,10.7L7.55,10.7C7.55,10.387 7.481,10.094 7.344,9.819C7.219,9.531 7.031,9.281 6.781,9.069L5.975,8.45L5.975,3.725C5.975,3.537 5.906,3.381 5.769,3.256C5.644,3.119 5.488,3.05 5.3,3.05C5.113,3.05 4.95,3.119 4.813,3.256C4.688,3.381 4.625,3.537 4.625,3.725L4.625,8.45L3.838,9.069C3.588,9.294 3.394,9.55 3.256,9.837C3.119,10.125 3.05,10.413 3.05,10.7Z" />
+ </group>
+</vector>
diff --git a/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml b/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml
index dddad814b451..bcdbd6f41309 100644
--- a/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml
+++ b/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml
@@ -16,12 +16,10 @@ Copyright (C) 2023 The Android Open Source Project
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportWidth="20"
- android:viewportHeight="20">
+ android:viewportWidth="16"
+ android:viewportHeight="16">
<path
- android:pathData="M15.333,5.333V4.667C15.333,4.3 15.033,4 14.667,4L13.333,4C12.967,4 12.667,4.3 12.667,4.667V5.333H12V8C12,8.367 12.3,8.667 12.667,8.667H13.333L13.333,13.333C13.333,14.067 12.733,14.667 12,14.667C11.267,14.667 10.667,14.067 10.667,13.333L10.667,11.333V6.667C10.667,5.193 9.473,4 8,4C6.527,4 5.333,5.193 5.333,6.667L5.333,11.333H4.667C4.3,11.333 4,11.633 4,12L4,14.667H4.667V15.333C4.667,15.7 4.967,16 5.333,16H6.667C7.033,16 7.333,15.7 7.333,15.333V14.667H8L8,12C8,11.633 7.7,11.333 7.333,11.333H6.667L6.667,6.667C6.667,5.933 7.267,5.333 8,5.333C8.733,5.333 9.333,5.933 9.333,6.667V11.333L9.333,13.333C9.333,14.807 10.527,16 12,16C13.473,16 14.667,14.807 14.667,13.333L14.667,8.667H15.333C15.7,8.667 16,8.367 16,8V5.333H15.333Z"
+ android:pathData="M13.333,3.333V2.667C13.333,2.3 13.033,2 12.667,2L11.333,2C10.967,2 10.667,2.3 10.667,2.667V3.333H10V6C10,6.367 10.3,6.667 10.667,6.667H11.333L11.333,11.333C11.333,12.067 10.733,12.667 10,12.667C9.267,12.667 8.667,12.067 8.667,11.333L8.667,9.333V4.667C8.667,3.193 7.473,2 6,2C4.527,2 3.333,3.193 3.333,4.667L3.333,9.333H2.667C2.3,9.333 2,9.633 2,10L2,12.667H2.667V13.333C2.667,13.7 2.967,14 3.333,14H4.667C5.033,14 5.333,13.7 5.333,13.333V12.667H6L6,10C6,9.633 5.7,9.333 5.333,9.333H4.667L4.667,4.667C4.667,3.933 5.267,3.333 6,3.333C6.733,3.333 7.333,3.933 7.333,4.667L7.333,9.333L7.333,11.333C7.333,12.807 8.527,14 10,14C11.473,14 12.667,12.807 12.667,11.333L12.667,6.667H13.333C13.7,6.667 14,6.367 14,6V3.333H13.333Z"
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"/>
</vector>
-
-
diff --git a/core/res/res/drawable/ic_accessibility_generic.xml b/core/res/res/drawable/ic_accessibility_generic.xml
new file mode 100644
index 000000000000..68a89e688762
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_generic.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M5.6875,22.8235C4.9092,22.4776 4.8184,22.2615 2.8752,16.1257 1.8439,12.8691 1.0015,10.0882 1.0033,9.946 1.0137,9.1246 1.3166,8.8389 6.25,4.9976 9.2052,2.6966 11.2442,1.1943 11.5332,1.1049 11.8724,0.9999 12.1235,0.996 12.432,1.0907 12.9214,1.2408 22.3634,8.7104 22.6857,9.2024 23.1266,9.8752 23.0768,10.1907 22.0053,13.5155 19.0153,22.7935 19.1481,22.461 18.2853,22.8286 17.7053,23.0757 6.2446,23.0711 5.6875,22.8235Z"
+ android:strokeWidth="0.31999999"
+ android:fillColor="#ced6da"/>
+ <path
+ android:pathData="M10.0615,19.3507C10.028,19.2609 9.9864,17.362 9.9691,15.1308L9.9375,11.0741 8.5,10.853c-2.1981,-0.3381 -2.1924,-0.3355 -2.1619,-0.978 0.0141,-0.2963 0.074,-0.587 0.1331,-0.6462 0.06,-0.06 0.7667,0.0113 1.5994,0.1614 2.1217,0.3824 5.7371,0.3824 7.8588,0 0.8206,-0.1479 1.5349,-0.2259 1.5874,-0.1733 0.0525,0.0526 0.1334,0.3334 0.1799,0.624 0.078,0.4881 0.0598,0.5378 -0.2384,0.6512 -0.1776,0.0675 -1.0143,0.2259 -1.8593,0.352l-1.5364,0.2293 -0.0625,4.182 -0.0625,4.182l-0.625,0 -0.625,0l-0.0625,-1.875 -0.0625,-1.875l-0.5625,0L11.4375,15.6875l-0.0625,1.875 -0.0625,1.875 -0.595,0.0382c-0.4038,0.0259 -0.6146,-0.0143 -0.6559,-0.125zM11.3716,8.912c-0.4861,-0.3351 -0.6133,-0.5622 -0.6176,-1.1029 -0.0047,-0.6005 0.2255,-0.9684 0.739,-1.1811 0.8994,-0.3726 1.7571,0.2075 1.7571,1.1885 0,0.4533 -0.0659,0.5905 -0.4418,0.9206 -0.5007,0.4396 -0.9697,0.4967 -1.4366,0.1749z"
+ android:strokeWidth="0.31999999"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/core/res/res/layout/accessibility_service_warning.xml b/core/res/res/layout/accessibility_service_warning.xml
new file mode 100644
index 000000000000..0381facd3628
--- /dev/null
+++ b/core/res/res/layout/accessibility_service_warning.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:textDirection="locale"
+ android:scrollbarStyle="outsideOverlay"
+ android:gravity="top">
+
+ <LinearLayout
+ android:accessibilityDataSensitive="yes"
+ style="@style/AccessibilityDialog">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp">
+
+ <ImageView
+ android:id="@+id/accessibility_permissionDialog_icon"
+ style="@style/AccessibilityDialogServiceIcon" />
+
+ <TextView
+ android:id="@+id/accessibility_permissionDialog_title"
+ style="@style/AccessibilityDialogTitle" />
+
+ <TextView
+ android:id="@+id/permissionDialog_description"
+ android:text="@string/accessibility_service_warning_description"
+ style="@style/AccessibilityDialogDescription" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="24dp" >
+
+ <ImageView
+ android:id="@+id/controlScreen_icon"
+ android:src="@drawable/ic_visibility"
+ style="@style/AccessibilityDialogIcon" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/controlScreen_title"
+ android:text="@string/accessibility_service_screen_control_title"
+ style="@style/AccessibilityDialogPermissionTitle" />
+
+ <TextView
+ android:id="@+id/controlScreen_description"
+ android:text="@string/accessibility_service_screen_control_description"
+ style="@style/AccessibilityDialogPermissionDescription" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="24dp" >
+
+ <ImageView
+ android:id="@+id/performAction_icon"
+ android:src="@drawable/ic_pan_tool"
+ style="@style/AccessibilityDialogIcon" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/performAction_title"
+ android:text="@string/accessibility_service_action_perform_title"
+ style="@style/AccessibilityDialogPermissionTitle" />
+
+ <TextView
+ android:id="@+id/performAction_description"
+ android:text="@string/accessibility_service_action_perform_description"
+ style="@style/AccessibilityDialogPermissionDescription" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Buttons on bottom of dialog -->
+ <LinearLayout
+ style="@style/AccessibilityDialogButtonList">
+
+ <Space
+ style="@style/AccessibilityDialogButtonBarSpace"/>
+
+ <Button
+ android:id="@+id/accessibility_permission_enable_allow_button"
+ android:text="@string/accessibility_dialog_button_allow"
+ style="@style/AccessibilityDialogButton" />
+
+ <Button
+ android:id="@+id/accessibility_permission_enable_deny_button"
+ android:text="@string/accessibility_dialog_button_deny"
+ style="@style/AccessibilityDialogButton" />
+
+ <Button
+ android:id="@+id/accessibility_permission_enable_uninstall_button"
+ android:text="@string/accessibility_dialog_button_uninstall"
+ android:visibility="gone"
+ style="@style/AccessibilityDialogButton" />
+ </LinearLayout>
+ </LinearLayout>
+
+</ScrollView>
diff --git a/core/res/res/values-af-watch/strings.xml b/core/res/res/values-af-watch/strings.xml
index 15bc1f35ba91..a575c77e3731 100644
--- a/core/res/res/values-af-watch/strings.xml
+++ b/core/res/res/values-af-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensors"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Nood-SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Maak tans gereed om op te dateer"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-stelselopdatering"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Kies invoer"</string>
</resources>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 50372395a1dc..f9ab7dd283c9 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Dit kan jou interaksies met \'n app of \'n hardewaresensor naspoor en namens jou met apps interaksie hê."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Laat toe"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Weier"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op \'n kenmerk om dit te begin gebruik:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kies kenmerke om saam met die toeganklikheidknoppie te gebruik"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kies kenmerke om saam met die volumesleutelkortpad te gebruik"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Naweek"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Geleentheid"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slaap"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Daar is \'n interne probleem met jou toestel. Kontak jou vervaardiger vir besonderhede."</string>
@@ -2336,6 +2344,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoon is geblokkeer"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kan nie na skerm weerspieël nie"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gebruik ’n ander kabel en probeer weer"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Jou toestel is te warm en kan nie na die skerm weerspieël totdat dit afgekoel het nie"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel steun dalk nie skerms nie"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Jou USB-C-kabel koppel dalk nie behoorlik aan skerms nie"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-am-watch/strings.xml b/core/res/res/values-am-watch/strings.xml
index 243ba55ce532..ed7dbe021106 100644
--- a/core/res/res/values-am-watch/strings.xml
+++ b/core/res/res/values-am-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"አነፍናፊዎች"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"የነፍስ አድን ድንገተኛ ጥሪ"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"ለማዘመን በመዘጋጀት ላይ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"የWear OS ስርዓት ዝማኔ"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ግብዓትን ይምረጡ"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9a8bb6233172..5ac3225274d4 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ከመተግበሪያ ጋር ወይም የሃርድዌር ዳሳሽ ጋር እርስዎ ያልዎትን መስተጋብሮች ዱካ መከታተል እና በእርስዎ ምትክ ከመተግበሪያዎች ጋር መስተጋብር መፈጸም ይችላል።"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ፍቀድ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ከልክል"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"አንድ ባህሪን መጠቀም ለመጀመር መታ ያድርጉት፦"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"በተደራሽነት አዝራር የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"በድምጽ ቁልፍ አቋራጭ የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"የሳምንት እረፍት ቀናት"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ክስተት"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"መተኛት"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንድ ድምጾችን እየዘጋ ነው"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ፣ የፋብሪካ ውሂብ ዳግም እስኪያስጀምሩት ድረስ ላይረጋጋ ይችላል።"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ። ዝርዝሮችን ለማግኘት አምራችዎን ያነጋግሩ።"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ማይክሮፎን ታግዷል"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ወደ ማሳያ ማንጸባረቅ አልተቻለም"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"የተለየ ገመድ ይጠቀሙ እና እንደገና ይሞክሩ"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ገመድ ማሳያዎችን ላይደግፍ ይችላል"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"የእርስዎ USB-C ገመድ ከማሳያዎች ጋር በትክክል ላይገናኝ ይችላል"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-ar-watch/strings.xml b/core/res/res/values-ar-watch/strings.xml
index 2a8248b7ffe6..2b1ed9501380 100644
--- a/core/res/res/values-ar-watch/strings.xml
+++ b/core/res/res/values-ar-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"أجهزة الاستشعار"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"اتصالات الطوارئ"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"جارٍ التحضير لإجراء التحديث"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"‏تحديث نظام التشغيل Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"اختيار أسلوب الإدخال"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index f6d7c647c84f..537beece8cfa 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1694,7 +1694,7 @@
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"إزالة"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
<string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"هل تريد مواصلة الاستماع بصوت عالٍ؟\n\nكان مستوى صوت سمّاعة الرأس مرتفعًا لمدة أطول مما يُنصَح به، وقد يضر هذا بسمعك."</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"تم رصد صوت مرتفع.\n\nكان مستوى صوت سمّاعة الرأس مرتفعًا لمدة أطول مما يُنصَح به، وقد يضر هذا بسمعك."</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"تم رصد صوت مرتفع\n\nكان مستوى صوت سمّاعة الرأس مرتفعًا لمدة أطول مما يُنصَح به، وقد يضر هذا بسمعك."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"هل تريد استخدام اختصار \"سهولة الاستخدام\"؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"عند تفعيل الاختصار، يؤدي الضغط على زرّي التحكّم في مستوى الصوت معًا لمدة 3 ثوانٍ إلى تفعيل إحدى ميزات إمكانية الوصول."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"هل تريد تفعيل الاختصار لميزات إمكانية الوصول؟"</string>
@@ -1714,6 +1714,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"قد يؤدي ذلك إلى السماح للميزة بتتبّع تفاعلاتك مع تطبيق أو جهاز استشعار والتفاعل مع التطبيقات نيابةً عنك."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"سماح"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"رفض"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"انقر على ميزة لبدء استخدامها:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"اختيار الميزات التي تريد استخدامها مع زر أدوات تمكين الوصول"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"اختيار الميزات التي تريد استخدامها مع اختصار مفتاح التحكّم في مستوى الصوت"</string>
@@ -1908,6 +1912,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"نهاية الأسبوع"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"حدث"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"النوم"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"حدثت مشكلة داخلية في جهازك. يمكنك الاتصال بالمصنِّع للحصول على تفاصيل."</string>
@@ -2340,6 +2348,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"تم حظر الميكروفون."</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"يتعذّر إجراء نسخ مطابق لمحتوى جهازك إلى الشاشة"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"يُرجى استخدام كابل آخر وإعادة المحاولة."</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"قد لا يتوافق الكابل مع الشاشات"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"‏قد لا يتم توصيل الكابل المزوَّد بمنفذ USB-C بالشاشات بشكل صحيح."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-as-watch/strings.xml b/core/res/res/values-as-watch/strings.xml
index d65b58d647a3..0014bb82a2ce 100644
--- a/core/res/res/values-as-watch/strings.xml
+++ b/core/res/res/values-as-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ছেন্সৰসমূহ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"জৰুৰীকালীন SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"আপডে’ট কৰিবলৈ সাজু কৰি থকা হৈছে"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OSৰ ছিষ্টেম আপডে’ট"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ইনপুট বাছনি কৰক"</string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 517ae9aabbf8..a5d2ee415a3f 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ই আপুনি কোনো এপ্ বা হার্ডৱেৰ ছেন্সৰৰ সৈতে কৰা ভাব-বিনিময় আৰু আপোনাৰ হৈ অন্য কোনো লোকে এপৰ সৈতে কৰা ভাব-বিনিময় ট্ৰেক কৰিব পাৰে।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"অনুমতি দিয়ক"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"অস্বীকাৰ কৰক"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনো এটা সুবিধা ব্যৱহাৰ কৰিবলৈ সেইটোত টিপক:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"সাধ্য-সুবিধা বুটামটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কীৰ শ্বৰ্টকাটটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহ অন্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"কার্যক্ৰম"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"নিদ্ৰাৰত"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে। সবিশেষ জানিবৰ বাবে আপোনাৰ ডিভাইচ নির্মাতাৰ সৈতে যোগাযোগ কৰক।"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"মাইক্ৰ’ফ’নটো অৱৰোধ কৰি থোৱা আছে"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"সংযুক্ত ডিছপ্লে’ উপলব্ধ নহয়"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"অন্য এডাল কে’বল ব্যৱহাৰ কৰি পুনৰ চেষ্টা কৰক"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"কে’বলে ডিছপ্লে’ সমৰ্থন নকৰিবও পাৰে"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"আপোনাৰ USB-C কে’বল ডিছপ্লে’ৰ সৈতে সঠিকভাৱে সংযোগ নহ’বও পাৰে"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-az-watch/strings.xml b/core/res/res/values-az-watch/strings.xml
index 15b640b14257..62a048385782 100644
--- a/core/res/res/values-az-watch/strings.xml
+++ b/core/res/res/values-az-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorlar"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Fövqəladə halda SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Güncəllənməyə hazırlanır"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS sistem güncəlləməsi"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Daxiletmə seçin"</string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c0115550c35a..87e1c9f28fff 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tətbiq və sensorlarla əlaqələrinizi izləyib tətbiqlərə adınızdan əmrlər verə bilər."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İcazə verin"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"İmtina edin"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Həftə sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tədbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Yuxu vaxtı"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızın daxili problemi var və istehsalçı sıfırlanması olmayana qədər qeyri-stabil ola bilər."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Cihazınızın daxili problemi var. Əlavə məlumat üçün istehsalçı ilə əlaqə saxlayın."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon blok edilib"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Displeydə əks etdirmək olmur"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Başqa kabel istifadə edin və yenidən cəhd edin"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel displeyləri dəstəkləməyə bilər"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C kabeli displeylərə düzgün qoşulmaya bilər"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"İkili ekran"</string>
diff --git a/core/res/res/values-b+sr+Latn-watch/strings.xml b/core/res/res/values-b+sr+Latn-watch/strings.xml
index fc0b05dd95af..462dad52aec6 100644
--- a/core/res/res/values-b+sr+Latn-watch/strings.xml
+++ b/core/res/res/values-b+sr+Latn-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Hitna pomoć"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Ažuriranje se priprema"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Ažuriranje sistema za Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Odaberite unos"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 48b5c02c6286..06d73eedbaa9 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Može da prati interakcije sa aplikacijom ili senzorom hardvera i koristi aplikacije umesto vas."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dozvoli"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite neku funkciju da biste počeli da je koristite:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije za prečicu tasterom jačine zvuka"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Došlo je do internog problema u vezi sa uređajem. Potražite detalje od proizvođača."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Preslikavanje na ekran nije moguće"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Upotrebite drugi kabl i probajte ponovo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabl ne podržava ekrane"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C kabl se ne povezuje pravilno sa ekranima"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-be-watch/strings.xml b/core/res/res/values-be-watch/strings.xml
index bd3f77a00ce9..6bd8adbe31ee 100644
--- a/core/res/res/values-be-watch/strings.xml
+++ b/core/res/res/values-be-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Датчыкі"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Экстранны выклік"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Ідзе падрыхтоўка да абнаўлення"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Абнаўленне сістэмы Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Выберыце спосаб уводу"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 12effa0c6485..7ff6838951fb 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Гэта функцыя можа адсочваць вашы ўзаемадзеянні з праграмай ці датчыкам апаратнага забеспячэння і ўзаемадзейнічаць з праграмамі ад вашага імя."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дазволіць"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Адмовіць"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Каб пачаць выкарыстоўваць функцыю, націсніце на яе:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберыце функцыі для выкарыстання з клавішай гучнасці"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выхадныя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Падзея"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Рэжым сну"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> выключае некаторыя гукі"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"На вашай прыладзе ўзнікла ўнутраная праблема, і яна можа працаваць нестабільна, пакуль вы не зробіце скід да заводскіх налад."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"На вашай прыладзе ўзнікла ўнутраная праблема. Для атрымання дадатковай інфармацыі звярніцеся да вытворцы."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Мікрафон заблакіраваны"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не ўдалося прадубліраваць змесціва на дысплэі"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Паспрабуйце скарыстаць іншы кабель"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Магчыма, кабель несумяшчальны з дысплэямі"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Магчыма, кабель USB-C не падыходзіць да дысплэяў"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-bg-watch/strings.xml b/core/res/res/values-bg-watch/strings.xml
index e72ec966dc14..6c8cecd7f630 100644
--- a/core/res/res/values-bg-watch/strings.xml
+++ b/core/res/res/values-bg-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Сензори"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Спешно повикване SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Подготвя се за актуализация"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Системна актуализация за Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Избиране на метод на въвеждане"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d18e4bc5f00e..304c2a0d8fae 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Услугата може да проследява взаимодействията ви с дадено приложение или хардуерен сензор, както и да взаимодейства с приложенията от ваше име."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Разреш."</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Отказ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Докоснете дадена функция, за да започнете да я използвате:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Избиране на функции, които да използвате с бутона за достъпност"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Избиране на функции, които да използвате с прекия път чрез бутона за силата на звука"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Събота и неделя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Събитие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Време за сън"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушава някои звуци"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Възникна вътрешен проблем с устройството ви. То може да е нестабилно, докато не възстановите фабричните настройки."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Възникна вътрешен проблем с устройството ви. За подробности се свържете с производителя."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофонът е блокиран"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се копира огледално на дисплея"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Използвайте друг кабел и опитайте отново"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабелът не поддържа дисплеи"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C кабелът ви може да не се свързва правилно с дисплеи"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-bn-watch/strings.xml b/core/res/res/values-bn-watch/strings.xml
index 9b6ad6b86464..fb1c2838c6af 100644
--- a/core/res/res/values-bn-watch/strings.xml
+++ b/core/res/res/values-bn-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"সেন্সরগুলি"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ইমারজেন্সি SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"আপডেটের প্রস্তুতি চলছে"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS সিস্টেম আপডেট"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ইনপুট বেছে নিন"</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 859f37d026aa..1c6fa8da2bb4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"এটি কোনও একটি অ্যাপের সাথে অথবা হার্ডওয়্যার সেন্সরের সাথে আপনার ইন্টার‍্যাকশন ট্র্যাক করতে এবং আপনার হয়ে বিভিন্ন অ্যাপের সাথে ইন্টার‍্যাক্ট করতে পারে।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"অনুমতি দিন"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"খারিজ করুন"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনও ফিচার ব্যবহার করা শুরু করতে, সেটিতে ট্যাপ করুন:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"অ্যাক্সেসিবিলিটি বোতামের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কী শর্টকাটের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহান্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ইভেন্ট"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ঘুমানোর সময়"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> কিছু সাউন্ডকে মিউট করে দিচ্ছে"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে৷ বিস্তারিত জানার জন্য প্রস্তুতকারকের সাথে যোগাযোগ করুন৷"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"মাইক্রোফোন ব্লক করা হয়েছে"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ডিসপ্লে মিরর করা যাচ্ছে না"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"অন্য কোনও কেবল ব্যবহার করে আবার চেষ্টা করুন"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"কেবল, ডিসপ্লের সাথে কাজ নাও করতে পারে"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"আপনার USB-C কেবল, ডিসপ্লেতে সঠিকভাবে কানেক্ট নাও হতে পারে"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-bs-watch/strings.xml b/core/res/res/values-bs-watch/strings.xml
index e4a774ab799f..5124e4e26768 100644
--- a/core/res/res/values-bs-watch/strings.xml
+++ b/core/res/res/values-bs-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Hitni pozivi"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Priprema za ažuriranje"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Ažuriranje Wear OS sistema"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Odaberite unos"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 20f1d68a2fa0..c3f431a32ea5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Može pratiti vaše interakcije s aplikacijom ili hardverskim senzorom te ostvariti interakciju s aplikacijama umjesto vas."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dozvoli"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite funkciju da je počnete koristiti:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti s dugmetom Pristupačnost"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije koje ćete koristiti pomoću prečice tipke za jačinu zvuka"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Postoji problem u vašem uređaju. Za više informacija obratite se proizvođaču."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nije moguće preslikati na ekran"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Upotrijebite drugi kabl i pokušajte ponovo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabl možda neće podržavati ekrane"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C kabl se možda neće pravilno povezati s ekranima"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ca-watch/strings.xml b/core/res/res/values-ca-watch/strings.xml
index 98f1daac2fd9..bc0f29dfe93b 100644
--- a/core/res/res/values-ca-watch/strings.xml
+++ b/core/res/res/values-ca-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensors"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergència SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"S\'està preparant per a l\'actualització"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Actualització del sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Tria una entrada"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index dacdbf15baa5..fbbd2daf5dec 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pot fer un seguiment de les teves interaccions amb una aplicació o un sensor de maquinari, i interaccionar amb aplicacions en nom teu."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permet"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Denega"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una funció per començar a utilitzar-la:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Tria les funcions que vols utilitzar amb el botó d\'accessibilitat"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tria les funcions que vols utilitzar amb la drecera per a tecles de volum"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cap de setmana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esdeveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentre dormo"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"S\'ha produït un error intern al dispositiu i és possible que funcioni de manera inestable fins que restableixis les dades de fàbrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"S\'ha produït un error intern al dispositiu. Contacta amb el fabricant del dispositiu per obtenir més informació."</string>
@@ -1976,9 +1984,9 @@
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string>
<string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en continu. Primer concedeix el permís al teu dispositiu Android TV."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en continu. Primer concedeix el permís a la teva tauleta."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en continu. Primer concedeix el permís al teu telèfon."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís al teu dispositiu Android TV."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís a la teva tauleta."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís al teu telèfon."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al dispositiu Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho a la tauleta."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al telèfon."</string>
@@ -2322,7 +2330,7 @@
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No es pot accedir a la càmera del telèfon des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No es pot accedir a la càmera de la tauleta des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_secure_window" msgid="161700398158812314">"No s\'hi pot accedir mentre s\'està reproduint en continu. Prova-ho al telèfon."</string>
- <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant la reproducció en continu"</string>
+ <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant la reproducció en línia"</string>
<string name="system_locale_title" msgid="711882686834677268">"Valor predeterminat del sistema"</string>
<string name="default_card_name" msgid="9198284935962911468">"TARGETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
<string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permís del perfil del rellotge perquè l\'aplicació complementària gestioni els rellotges"</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"El micròfon està bloquejat"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No es pot projectar a la pantalla"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Utilitza un altre cable i torna-ho a provar"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"És possible que el cable no sigui compatible amb pantalles"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"És possible que el teu cable USB-C no es connecti correctament a les pantalles"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string>
diff --git a/core/res/res/values-cs-watch/strings.xml b/core/res/res/values-cs-watch/strings.xml
index 2ddd5347ef7c..eb7f0c8f774a 100644
--- a/core/res/res/values-cs-watch/strings.xml
+++ b/core/res/res/values-cs-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzory"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Tísňové volání"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Příprava na aktualizaci"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Aktualizace systému Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Vyberte metodu zadávání"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 29e00fa4d212..959b7bc0ceb7 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Služba může sledovat vaše interakce s aplikací nebo hardwarovým senzorem a komunikovat s aplikacemi namísto vás."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Povolit"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zakázat"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Chcete-li některou funkci začít používat, klepněte na ni:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vyberte funkce, které budete používat s tlačítkem přístupnosti"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vyberte funkce, které budete používat se zkratkou tlačítka hlasitosti"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Událost"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánek"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"V zařízení došlo k internímu problému. Další informace vám sdělí výrobce."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je zablokován"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nelze zrcadlit na displej"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Použijte jiný kabel a zkuste to znovu"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel možná nepodporuje displeje"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Váš kabel USB-C se možná nedokáže správně připojit k displejům"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-da-watch/strings.xml b/core/res/res/values-da-watch/strings.xml
index 26e7b7279bfe..099939a241e0 100644
--- a/core/res/res/values-da-watch/strings.xml
+++ b/core/res/res/values-da-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorer"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Alarm-SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Forbereder opdatering"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-systemopdatering"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Vælg indtastningsmetode"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 87703cdb2fd9..751831a329c0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Den kan spore dine interaktioner med en app eller en hardwaresensor og interagere med apps på dine vegne."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Tillad"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Afvis"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryk på en funktion for at bruge den:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vælg, hvilke funktioner du vil bruge med knappen til hjælpefunktioner"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vælg de funktioner, du vil bruge via lydstyrkeknapperne"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Begivenhed"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Der er et internt problem med enheden, og den vil muligvis være ustabil, indtil du gendanner fabriksdataene."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Der er et internt problem med enheden. Kontakt producenten for at få yderligere oplysninger."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofonen er blokeret"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Det er ikke muligt at spejle til skærmen"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Brug et andet kabel, og prøv igen"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kablet understøtter muligvis ikke skærme"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Dit USB-C-kabel kan muligvis ikke sluttes korrekt til skærmene"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-de-watch/strings.xml b/core/res/res/values-de-watch/strings.xml
index 370ea928e8e2..2c5a1beb64a1 100644
--- a/core/res/res/values-de-watch/strings.xml
+++ b/core/res/res/values-de-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensoren"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Notfall-SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Update wird vorbereitet"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-Systemupdate"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Eingabe auswählen"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2c1adb8b1464..56816fbfd346 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1689,7 +1689,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Entfernen"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Weiter mit hoher Lautstärke hören?\n\nDeine Kopfhörer sind schon länger als empfohlen auf hohe Lautstärke eingestellt – das kann deinem Hörvermögen schaden"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Weiter in hoher Lautstärke hören?\n\nDeine Kopfhörer sind schon länger als empfohlen auf hohe Lautstärke eingestellt – das kann deinem Hörvermögen schaden"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Hohe Lautstärke erkannt\n\nDu hast deine Kopfhörer lauter als empfohlen eingestellt – das kann deinem Hörvermögen schaden"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Verknüpfung für Bedienungshilfen verwenden?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten."</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Die Funktion kann deine Interaktionen mit einer App oder einem Hardwaresensor verfolgen und in deinem Namen mit Apps interagieren."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Zulassen"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Ablehnen"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Zum Auswählen der gewünschten Funktion tippen:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funktionen auswählen, die du mit der Schaltfläche \"Bedienungshilfen\" verwenden möchtest"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funktionen für Verknüpfung mit Lautstärketaste auswählen"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wochenende"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Termin"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Schlafen"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Es liegt ein internes Problem mit deinem Gerät vor. Bitte wende dich diesbezüglich an den Hersteller."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon ist blockiert"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kann nicht auf das Display gespiegelt werden"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Verwende ein anderes Kabel und versuch es noch einmal"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel unterstützt eventuell keine Bildschirme"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Dein USB-C-Kabel ist möglicherweise nicht zum Verbinden von Bildschirmen geeignet"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-el-watch/strings.xml b/core/res/res/values-el-watch/strings.xml
index 55de5f2c9671..b3fb77e46087 100644
--- a/core/res/res/values-el-watch/strings.xml
+++ b/core/res/res/values-el-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Αισθητήρες"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Έκτακτη ανάγκη SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Προετοιμασία για ενημέρωση"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Ενημέρωση συστήματος Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Επιλογή εισόδου"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index af53ddfcfee8..7784e952ed3d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Μπορεί να παρακολουθήσει τις αλληλεπιδράσεις σας με μια εφαρμογή ή έναν αισθητήρα εξοπλισμού και να αλληλεπιδράσει με εφαρμογές εκ μέρους σας."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Ναι"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Όχι"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Πατήστε μια λειτουργία για να ξεκινήσετε να τη χρησιμοποιείτε:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με το κουμπί προσβασιμότητας."</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με τη συντόμευση κουμπιού έντασης ήχου"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Σαββατοκύριακο"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Συμβάν"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ύπνος"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"Το τρίτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους ήχους σε σίγαση"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας και ενδέχεται να είναι ασταθής μέχρι την επαναφορά των εργοστασιακών ρυθμίσεων."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας. Επικοινωνήστε με τον κατασκευαστή σας για λεπτομέρειες."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Το μικρόφωνο έχει αποκλειστεί"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Δεν είναι δυνατός ο κατοπτρισμός στην οθόνη"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Χρησιμοποιήστε άλλο καλώδιο και δοκιμάστε ξανά"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Το καλώδιο μπορεί να μην υποστηρίζει οθόνες"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Το καλώδιο USB-C που έχετε ίσως να μην μπορεί να συνδεθεί σωστά σε οθόνες"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Διπλή οθόνη"</string>
diff --git a/core/res/res/values-en-rAU-watch/strings.xml b/core/res/res/values-en-rAU-watch/strings.xml
index e550f9e9db0a..5008face4098 100644
--- a/core/res/res/values-en-rAU-watch/strings.xml
+++ b/core/res/res/values-en-rAU-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensors"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergency SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparing to update"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS system update"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Choose input"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 8917a8203285..72f907cd9484 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"It can track your interactions with an app or a hardware sensor, and interact with apps on your behalf."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Allow"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Deny"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microphone is blocked"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Can\'t mirror to display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Please use a different cable and try again"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Cable may not support displays"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Your USB-C cable may not connect to displays properly"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 5a0ed859e441..3381e97a06d8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"It can track your interactions with an app or a hardware sensor and interact with apps on your behalf."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Allow"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Deny"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the accessibility button"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
@@ -1904,6 +1908,8 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
+ <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
@@ -2336,6 +2342,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microphone is blocked"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Can\'t mirror to display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Use a different cable and try again"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Your device is too warm and can\'t mirror to the display until it cools down"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Cable may not support displays"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Your USB-C cable may not connect to displays properly"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-en-rGB-watch/strings.xml b/core/res/res/values-en-rGB-watch/strings.xml
index e550f9e9db0a..5008face4098 100644
--- a/core/res/res/values-en-rGB-watch/strings.xml
+++ b/core/res/res/values-en-rGB-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensors"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergency SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparing to update"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS system update"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Choose input"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bcf0790bae59..6975a65451c0 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"It can track your interactions with an app or a hardware sensor, and interact with apps on your behalf."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Allow"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Deny"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microphone is blocked"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Can\'t mirror to display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Please use a different cable and try again"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Cable may not support displays"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Your USB-C cable may not connect to displays properly"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-en-rIN-watch/strings.xml b/core/res/res/values-en-rIN-watch/strings.xml
index e550f9e9db0a..5008face4098 100644
--- a/core/res/res/values-en-rIN-watch/strings.xml
+++ b/core/res/res/values-en-rIN-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensors"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergency SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparing to update"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS system update"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Choose input"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7ebffc6895c7..8f63abda4505 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"It can track your interactions with an app or a hardware sensor, and interact with apps on your behalf."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Allow"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Deny"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microphone is blocked"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Can\'t mirror to display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Please use a different cable and try again"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Cable may not support displays"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Your USB-C cable may not connect to displays properly"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index b739768c2068..ac2d58c77efa 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‎‎It can track your interactions with an app or a hardware sensor, and interact with apps on your behalf.‎‏‎‎‏‎"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎Allow‎‏‎‎‏‎"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎Deny‎‏‎‎‏‎"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎Tap a feature to start using it:‎‏‎‎‏‎"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎Choose features to use with the accessibility button‎‏‎‎‏‎"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎Choose features to use with the volume key shortcut‎‏‎‎‏‎"</string>
@@ -1904,6 +1908,8 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎Weekend‎‏‎‎‏‎"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎Event‎‏‎‎‏‎"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎Sleeping‎‏‎‎‏‎"</string>
+ <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎On‎‏‎‎‏‎"</string>
+ <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎Off‎‏‎‎‏‎"</string>
<string name="muted_by" msgid="91464083490094950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="THIRD_PARTY">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is muting some sounds‎‏‎‎‏‎"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎There\'s an internal problem with your device, and it may be unstable until you factory data reset.‎‏‎‎‏‎"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‎There\'s an internal problem with your device. Contact your manufacturer for details.‎‏‎‎‏‎"</string>
@@ -2336,6 +2342,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎Microphone is blocked‎‏‎‎‏‎"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎Can\'t mirror to display‎‏‎‎‏‎"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎Use a different cable and try again‎‏‎‎‏‎"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎Your device is too warm and can\'t mirror to the display until it cools down‎‏‎‎‏‎"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎Cable may not support displays‎‏‎‎‏‎"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‏‎Your USB-C cable may not connect to displays properly‎‏‎‎‏‎"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎Dual screen‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS-watch/strings.xml b/core/res/res/values-es-rUS-watch/strings.xml
index 5c5b66020fd9..852ee8953ac9 100644
--- a/core/res/res/values-es-rUS-watch/strings.xml
+++ b/core/res/res/values-es-rUS-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergencia SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparando la actualización"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Actualización del sistema de Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Elige una entrada"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ca9ff13550d3..cd83e421c92e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Puede realizar el seguimiento de tus interacciones con una app o un sensor de hardware, así como interactuar con las apps por ti."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Rechazar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Presiona una función para comenzar a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona las funciones a utilizar con el botón de accesibilidad"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona las funciones a usar con las teclas de volumen"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Existe un problema interno con el dispositivo, de modo que el dispositivo puede estar inestable hasta que restablezcas la configuración de fábrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Existe un problema interno con el dispositivo. Comunícate con el fabricante para obtener más información."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"El micrófono está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No se puede duplicar la pantalla"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Usa un cable diferente y vuelve a intentarlo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Es posible que el cable no admita pantallas"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Es posible que el cable USB-C no se conecte a las pantallas de manera adecuada"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-es-watch/strings.xml b/core/res/res/values-es-watch/strings.xml
index 1877b4255da3..f0d86d9a607d 100644
--- a/core/res/res/values-es-watch/strings.xml
+++ b/core/res/res/values-es-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergencia SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparando la actualización"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Actualización del sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Elige un método de entrada"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b76229a8563f..bfd877eb443f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Puede registrar tus interacciones con una aplicación o un sensor de hardware, así como interactuar con las aplicaciones en tu nombre."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Denegar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una función para empezar a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona qué funciones usar con el botón de accesibilidad"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona qué funciones usar con la tecla de volumen"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmiendo"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Se ha producido un problema interno en el dispositivo y es posible que este no sea estable hasta que restablezcas el estado de fábrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Se ha producido un problema interno en el dispositivo. Ponte en contacto con el fabricante para obtener más información."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"El micrófono está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No se puede proyectar a la pantalla"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Usa otro cable y vuelve a intentarlo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"El cable puede no ser compatible con pantallas"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Puede que tu cable USB‑C no sea adecuado para conectarse a pantallas"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-et-watch/strings.xml b/core/res/res/values-et-watch/strings.xml
index 09ff2e59cc4f..1f905bd41e33 100644
--- a/core/res/res/values-et-watch/strings.xml
+++ b/core/res/res/values-et-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Andurid"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Hädaabikõne"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Värskendamiseks ettevalmistamine"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-i süsteemivärskendus"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Valige sisend"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4a8666ee1459..dbb7663e0d0e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"See saab jälgida teie suhtlust rakenduse või riistvaraanduriga ja teie eest rakendustega suhelda."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Luba"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Keela"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Puudutage funktsiooni, et selle kasutamist alustada."</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valige helitugevuse nupu otsetee funktsioonid"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nädalavahetus"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sündmus"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Magamine"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Seadmes ilmnes sisemine probleem ja seade võib olla ebastabiilne seni, kuni lähtestate seadme tehase andmetele."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Seadmes ilmnes sisemine probleem. Üksikasjaliku teabe saamiseks võtke ühendust tootjaga."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon on blokeeritud"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ei saa ekraanile peegeldada"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Kasutage teist kaablit ja proovige uuesti"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kaabel ei pruugi ekraane toetada"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Teie USB-C-kaabel ei pruugi ekraanidega õigesti ühendust luua"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 86eaf0a08e1d..a35236f9dd5c 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Aplikazioekin edo hardware-sentsoreekin dituzun interakzioen jarraipena egin dezake, eta zure izenean beste aplikazio batzuekin interakzioan jardun."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Baimendu"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Ukatu"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Eginbide bat erabiltzen hasteko, saka ezazu:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoiarekin"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Aukeratu zein eginbide erabili nahi duzun bolumen-botoien lasterbidearekin"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Asteburua"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Gertaera"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Lo egiteko"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Barneko arazo bat dago zure gailuan. Xehetasunak jakiteko, jarri fabrikatzailearekin harremanetan."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Blokeatuta dago mikrofonoa"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ezin da islatu pantailan"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Erabili beste kable bat eta saiatu berriro"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Baliteke kablea pantailekin bateragarria ez izatea"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Baliteke USB-C kablea behar bezala ez konektatzea pantailetara"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-fa-watch/strings.xml b/core/res/res/values-fa-watch/strings.xml
index 0607c373cf49..59344f5f06bf 100644
--- a/core/res/res/values-fa-watch/strings.xml
+++ b/core/res/res/values-fa-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"حسگرها"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"کمک اضطراری"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"درحال آماده‌سازی برای به‌روزرسانی"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"‏به‌روزرسانی سیستم Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"انتخاب ورودی"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index c5a2ee62d891..c9df9d1fbd53 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -166,7 +166,7 @@
<string name="httpErrorTimeout" msgid="7446272815190334204">"زمان اتصال به سرور تمام شده است."</string>
<string name="httpErrorRedirectLoop" msgid="8455757777509512098">"این صفحه دارای تعداد بسیار زیادی تغییر مسیر سرور است."</string>
<string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"‏پروتکل پشتیبانی نمی‌‎شود."</string>
- <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"اتصال امن ایجاد نشد."</string>
+ <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"اتصال ایمن ایجاد نشد."</string>
<string name="httpErrorBadUrl" msgid="754447723314832538">"به‌دلیل نامعتبر بودن نشانی اینترنتی، صفحه باز نمی‌شود."</string>
<string name="httpErrorFile" msgid="3400658466057744084">"دسترسی به فایل انجام نشد."</string>
<string name="httpErrorFileNotFound" msgid="5191433324871147386">"فایل درخواستی پیدا نشد."</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"این عملکرد می‌تواند با برنامه یا حسگری سخت‌افزاری تعاملاتتان را ردیابی کند و ازطرف شما با برنامه‌ها تعامل داشته باشد."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"اجازه دادن"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مجاز نبودن"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن ضربه بزنید:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگی‌های موردنظر برای استفاده با دکمه دسترس‌پذیری"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگی‌های موردنظر برای استفاده با میان‌بر کلید میزان صدا"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"آخر هفته"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"رویداد"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"خوابیدن"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> درحال قطع کردن بعضی از صداهاست"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی داده‌های کارخانه انجام نگیرد، بی‌ثبات بماند."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"دستگاهتان یک مشکل داخلی دارد. برای جزئیات آن با سازنده‌تان تماس بگیرید."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"میکروفون مسدود شد"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"بازتاب دادن به نمایشگر ممکن نبود"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"از کابل دیگری استفاده کنید و دوباره امتحان کنید"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"شاید کابل از نمایشگر پشتیبانی نکند"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"‏کابل USB-C شما ممکن است به‌درستی به نمایشگرها وصل نشود"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"‫Dual screen"</string>
diff --git a/core/res/res/values-fi-watch/strings.xml b/core/res/res/values-fi-watch/strings.xml
index a8e873d63f5f..e2241752fb7f 100644
--- a/core/res/res/values-fi-watch/strings.xml
+++ b/core/res/res/values-fi-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Anturit"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Hätäpuhelut"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Valmistellaan päivitystä"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS ‑järjestelmäpäivitys"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Valitse syöttötapa"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4a08b9006696..69a65a2e5c2a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Se voi seurata toimintaasi sovelluksella tai laitteistoanturilla ja käyttää sovelluksia puolestasi."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Salli"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Estä"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Aloita ominaisuuden käyttö napauttamalla sitä:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valitse ominaisuudet, joita käytetään esteettömyyspainikkeella"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valitse ominaisuudet, joita käytetään äänenvoimakkuuspikanäppäimellä"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Viikonloppuna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tapahtuma"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Nukkuminen"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Laitteellasi on sisäinen ongelma, joka aiheuttaa epävakautta. Voit korjata tilanteen palauttamalla tehdasasetukset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Laitteesi yhdistäminen ei onnistu sisäisen virheen takia. Saat lisätietoja valmistajalta."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoni on estetty"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Näytön peilaaminen ei onnistu"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Käytä eri johtoa ja yritä uudelleen"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Johto ei ehkä tue näyttöjä"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C-johtosi ei ehkä yhdisty näyttöihin kunnolla"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Kaksoisnäyttö"</string>
diff --git a/core/res/res/values-fr-rCA-watch/strings.xml b/core/res/res/values-fr-rCA-watch/strings.xml
index babd6caf5943..7be006173f51 100644
--- a/core/res/res/values-fr-rCA-watch/strings.xml
+++ b/core/res/res/values-fr-rCA-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Capteurs"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Appel d\'urgence"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Préparation de la mise à jour en cours…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Mise à jour du système Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Sélectionner une entrée"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 1b637db1e0d7..acdfaab7ed01 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Cette fonctionnalité peut faire le suivi de vos interactions avec une application ou un capteur matériel et interagir avec des applications en votre nom."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Autoriser"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuser"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toucher une fonctionnalité pour commencer à l\'utiliser :"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser à l\'aide du bouton d\'accessibilité"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semaine"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Un problème interne est survenu avec votre appareil. Communiquez avec le fabricant pour obtenir plus de détails."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Le microphone est bloqué"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Impossible de dupliquer l\'écran"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Utilisez un câble différent et réessayez"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Le câble peut ne pas être compatible avec les écrans"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Votre câble USB-C peut ne pas se connecter correctement aux écrans"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-fr-watch/strings.xml b/core/res/res/values-fr-watch/strings.xml
index 130c466601d5..15d49be9481d 100644
--- a/core/res/res/values-fr-watch/strings.xml
+++ b/core/res/res/values-fr-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Capteurs"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS Urgence"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Préparation de la mise à jour…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Mise à jour du système Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Sélectionner une saisie"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4f8de0c36867..a96b17939908 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Le service peut suivre vos interactions avec une application ou un capteur matériel, et interagir avec des applications de votre part."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Autoriser"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuser"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Appuyez sur une fonctionnalité pour commencer à l\'utiliser :"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser avec le bouton Accessibilité"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Week-end"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne lié à votre appareil est survenu. Ce dernier risque d\'être instable jusqu\'à ce que vous rétablissiez la configuration d\'usine."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Un problème interne lié à votre appareil est survenu. Veuillez contacter le fabricant pour en savoir plus."</string>
@@ -2337,15 +2345,17 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Le micro est bloqué"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Duplication impossible"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Utilisez un autre câble et réessayez"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Le câble n\'est peut-être pas compatible avec les écrans"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Votre câble USB-C n\'est peut-être pas connecté correctement à l\'écran"</string>
- <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Double écran"</string>
- <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Double écran activé"</string>
+ <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
+ <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen activé"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise les deux écrans pour afficher du contenu"</string>
<string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"L\'appareil est trop chaud"</string>
- <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Double écran n\'est pas disponible, car votre téléphone surchauffe"</string>
- <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Double écran n\'est pas disponible"</string>
- <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Double écran n\'est pas disponible, car Économiseur de batterie est activé. Vous pouvez désactiver cette option dans les paramètres."</string>
+ <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen n\'est pas disponible, car votre téléphone surchauffe"</string>
+ <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen n\'est pas disponible"</string>
+ <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen n\'est pas disponible, car l\'économiseur de batterie est activé. Vous pouvez désactiver cette option dans les paramètres."</string>
<string name="device_state_notification_settings_button" msgid="691937505741872749">"Accédez aux paramètres"</string>
<string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Désactiver"</string>
<string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> configuré"</string>
diff --git a/core/res/res/values-gl-watch/strings.xml b/core/res/res/values-gl-watch/strings.xml
index f11990a760f0..c4f7a15abfbb 100644
--- a/core/res/res/values-gl-watch/strings.xml
+++ b/core/res/res/values-gl-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emerxencia SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparando todo para a actualización"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Actualización do sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Escoller o método de introdución de texto"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index b25cfcb82d1d..d001e90b23f7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pode facer un seguimento das túas interaccións cunha aplicación ou cun sensor de hardware, así como interactuar por ti coas aplicacións."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Denegar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocar unha función para comezar a utilizala:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escoller as funcións que queres utilizar co botón Accesibilidade"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolle as funcións que queres utilizar co atallo da tecla de volume"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmindo"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Produciuse un erro interno no teu dispositivo e quizais funcione de maneira inestable ata o restablecemento dos datos de fábrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Produciuse un erro interno co teu dispositivo. Contacta co teu fabricante para obter máis información."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"O micrófono está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Non se pode proxectar contido na pantalla"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Cambia de cable e téntao de novo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Pode que o cable non sexa compatible con pantallas"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"O teu cable USB-C pode que non se conecte ás pantallas de maneira adecuada"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-gu-watch/strings.xml b/core/res/res/values-gu-watch/strings.xml
index 331ffa73aa2e..1a88de425e10 100644
--- a/core/res/res/values-gu-watch/strings.xml
+++ b/core/res/res/values-gu-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"સેન્સર્સ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ઇમર્જન્સી સહાય"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"અપડેટ કરવાની તૈયારી કરી રહ્યાં છીએ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OSની સિસ્ટમ અપડેટ"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ઇનપુટ પસંદ કરો"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index fae5ebc3279c..8b54db5f3946 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"તે ઍપ અથવા હાર્ડવેર સેન્સર વડે તમારી ક્રિયાપ્રતિક્રિયાને ટ્રૅક કરી શકે છે અને તમારા વતી ઍપ સાથે ક્રિયાપ્રતિક્રિયા કરી શકે છે."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"મંજૂરી આપો"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"નકારો"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"સુવિધાનો ઉપયોગ શરૂ કરવા તેના પર ટૅપ કરો:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ઍક્સેસિબિલિટી બટન વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"વૉલ્યૂમ કી શૉર્ટકટ વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"સપ્તાહાંત"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ઇવેન્ટ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"નિષ્ક્રિય"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> અમુક અવાજોને મ્યૂટ કરે છે"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે અને જ્યાં સુધી તમે ફેક્ટરી ડેટા ફરીથી સેટ કરશો નહીં ત્યાં સુધી તે અસ્થિર રહી શકે છે."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે. વિગતો માટે તમારા નિર્માતાનો સંપર્ક કરો."</string>
@@ -2011,7 +2019,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ફોટો અને છબીઓ"</string>
<string name="app_category_social" msgid="2278269325488344054">"સામાજિક અને સંચાર"</string>
<string name="app_category_news" msgid="1172762719574964544">"સમાચાર અને સામાયિકો"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Maps અને નેવિગેશન"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"Maps અને નૅવિગેશન"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ઉત્પાદકતા"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"ઍક્સેસિબિલિટી"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ડિવાઇસ સ્ટૉરેજ"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"માઇક્રોફોનને બ્લૉક કરવામાં આવ્યો છે"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ડિસ્પ્લે પર મિરર કરી શકાતું નથી"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"બીજા કોઈ કેબલનો ઉપયોગ કરો અને ફરી પ્રયાસ કરો"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"શક્ય છે કે કેબલ કદાચ ડિસ્પ્લેને સપોર્ટ ન આપતો હોય"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"તમારો USB-C કેબલ કદાચ ડિસ્પ્લે સાથે યોગ્ય રીતે કનેક્ટ ન થાય"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-hi-watch/strings.xml b/core/res/res/values-hi-watch/strings.xml
index 75ff50db4c56..9cd56cfe4d52 100644
--- a/core/res/res/values-hi-watch/strings.xml
+++ b/core/res/res/values-hi-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"सेंसर"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"इमरजेंसी एसओएस"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"अपडेट के लिए तैयार हो रहा है"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS का सिस्टम अपडेट"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"इनपुट चुनें"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d4eb380098f8..87313a5f0ae9 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"यह आपके और किसी ऐप्लिकेशन या हार्डवेयर सेंसर के बीच होने वाले इंटरैक्शन को ट्रैक कर सकता है और आपकी तरफ़ से ऐप्लिकेशन के साथ इंटरैक्ट कर सकता है."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमति दें"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"इंकार करें"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"सुलभता बटन पर टैप करके, इस्तेमाल करने के लिए सुविधाएं चुनें"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"चुनें कि आवाज़ कम या ज़्यादा करने वाले बटन के शॉर्टकट से आप किन सुविधाओं का इस्तेमाल करना चाहते हैं"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"सप्ताहांत"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इवेंट"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"सोते समय"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> कुछ आवाज़ें म्‍यूट कर रहा है"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"आपके डिवाइस में कोई अंदरूनी समस्या है और यह तब तक ठीक नहीं होगी जब तक आप फ़ैक्‍टरी डेटा रीसेट नहीं करते."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"आपके डिवाइस के साथ कोई आंतरिक गड़बड़ी हुई. विवरणों के लिए अपने निर्माता से संपर्क करें."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"माइक्रोफ़ोन को ब्लॉक किया गया है"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"डिसप्ले का कॉन्टेंट नहीं दिखाया जा सकता"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"कोई दूसरा केबल इस्तेमाल करके फिर से कोशिश करें"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ऐसा हो सकता है कि केबल, डिसप्ले के साथ ठीक से काम न करे"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ऐसा हो सकता है कि यूएसबी-सी केबल, डिसप्ले के साथ ठीक से कनेक्ट न हो पाए"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-hr-watch/strings.xml b/core/res/res/values-hr-watch/strings.xml
index 237b4c0f9524..4e813bec108c 100644
--- a/core/res/res/values-hr-watch/strings.xml
+++ b/core/res/res/values-hr-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS poziv"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Priprema za ažuriranje"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Ažuriranje sustava Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Odaberite unos"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 81ac641a5e1e..c5e52bb9f8ad 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1690,7 +1690,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ukloni"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Želite li nastaviti slušati vrlo glasno?\n\nPojačana je glasnoća u slušalicama dulje nego što se preporučuje, a to vam može oštetiti sluh"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Želite li nastaviti slušati vrlo glasno?\n\nGlasnoća u slušalicama pojačana je dulje nego što se preporučuje, a to vam može oštetiti sluh"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Detektiran je glasan zvuk\n\nGlasnoća u slušalicama jača je od preporučene, a to vam može oštetiti sluh"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li upotrebljavati prečac za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad je taj prečac uključen, pritiskom na obje tipke za glasnoću na tri sekunde pokrenut će se značajka pristupačnosti."</string>
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Može pratiti vaše interakcije s aplikacijama ili senzorom uređaja i stupati u interakciju s aplikacijama u vaše ime."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dopusti"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite značajku da biste je počeli koristiti:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odabir značajki za upotrebu pomoću gumba za Pristupačnost"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odabir značajki za upotrebu pomoću prečaca tipki za glasnoću"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Na vašem uređaju postoji interni problem i možda neće biti stabilan dok ga ne vratite na tvorničko stanje."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Na vašem uređaju postoji interni problem. Obratite se proizvođaču za više pojedinosti."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Zrcaljenje na zaslon nije moguće"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Upotrijebite drugi kabel i pokušajte ponovno"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel možda ne podržava zaslone"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Vaš USB-C kabel možda nije ispravno povezan sa zaslonima"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dvostruki zaslon"</string>
diff --git a/core/res/res/values-hu-watch/strings.xml b/core/res/res/values-hu-watch/strings.xml
index 00c7b0421f2c..3c5d891e8619 100644
--- a/core/res/res/values-hu-watch/strings.xml
+++ b/core/res/res/values-hu-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Érzékelők"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Segélyhívás"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Felkészülés a frissítésre…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-rendszerfrissítés"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Bevitel kiválasztása"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9a389db091b7..39e49f67db64 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Követheti az alkalmazásokkal és hardveres érzékelőkkel való interakcióit, és műveleteket végezhet az alkalmazásokkal az Ön nevében."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Engedélyezés"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tiltás"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Koppintson valamelyik funkcióra a használatához:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kiválaszthatja a Kisegítő lehetőségek gombbal használni kívánt funkciókat"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kiválaszthatja a hangerőgombbal használni kívánt funkciókat"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hétvége"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esemény"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Alvás"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Belső probléma van az eszközzel, és instabil lehet, amíg vissza nem állítja a gyári adatokat."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Belső probléma van az eszközzel. A részletekért vegye fel a kapcsolatot a gyártóval."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"A mikrofon le van tiltva"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nem lehet tükrözni a kijelzőre"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Használjon másik kábelt, és próbálja újra"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Előfordulhat, hogy a kábel nem támogatja a kijelzőket"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Előfordulhat, hogy az USB-C kábellel nem csatlakoztathatók megfelelően a kijelzők"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-hy-watch/strings.xml b/core/res/res/values-hy-watch/strings.xml
index 3de85ed4b8ab..fbf9c4dcc3ca 100644
--- a/core/res/res/values-hy-watch/strings.xml
+++ b/core/res/res/values-hy-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Սենսորներ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Շտապ կանչեր"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Պատրաստվում է թարմացնել"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS համակարգի թարմացում"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Ընտրեք ներածման եղանակ"</string>
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 9c2c2c5daa2c..75c7c747d9e3 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Կարող է հետագծել ձեր գործողությունները հավելվածներում և սարքակազմի սենսորների վրա, ինչպես նաև հավելվածներում կատարել գործողություններ ձեր անունից։"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Թույլատրել"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Մերժել"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ընտրեք՝ որ գործառույթն օգտագործել"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Ընտրեք գործառույթները, որոնք կբացվեն «Հատուկ գործառույթներ» կոճակի միջոցով"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ընտրեք գործառույթները, որոնք կբացվեն ձայնի կարգավորման կոճակի միջոցով"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Շաբաթ-կիրակի"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Միջոցառում"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամանակ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Սարքում ներքին խնդիր է առաջացել: Մանրամասների համար կապվեք արտադրողի հետ:"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Խոսափողն արգելափակված է"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Չհաջողվեց հայելապատճենել էկրանին"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Օգտագործեք այլ մալուխ և նորից փորձեք"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Մալուխը կարող է համատեղելի չլինել էկրանների հետ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Հնարավոր է՝ USB-C մալուխը սխալ է միացված էկրանին"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-in-watch/strings.xml b/core/res/res/values-in-watch/strings.xml
index 34eaa91421ef..9e2771395403 100644
--- a/core/res/res/values-in-watch/strings.xml
+++ b/core/res/res/values-in-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensor"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Darurat SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Mempersiapkan update"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Update sistem Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Pilih input"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2d8e64291765..2d55f087600f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1689,7 +1689,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Hapus"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Tetap mendengarkan dengan volume tinggi?\n\nVolume headphone tinggi selama lebih lama dari yang direkomendasikan, yang dapat merusak pendengaran Anda"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Tetap mendengarkan dengan volume tinggi?\n\nVolume headphone tinggi untuk waktu lebih lama dari yang direkomendasikan, sehingga dapat merusak pendengaran Anda"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Suara keras terdeteksi\n\nVolume headphone lebih tinggi dari yang direkomendasikan, yang dapat merusak pendengaran Anda"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Aksesibilitas?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas."</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Voice Access dapat melacak interaksi Anda dengan aplikasi atau sensor hardware, dan berinteraksi dengan aplikasi untuk Anda."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Izinkan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tolak"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketuk fitur untuk mulai menggunakannya:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih fitur yang akan digunakan dengan tombol aksesibilitas"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih fitur yang akan digunakan dengan pintasan tombol volume"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Akhir pekan"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Ada masalah dengan perangkat. Hubungi produsen perangkat untuk informasi selengkapnya."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon diblokir"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Tidak dapat mencerminkan ke layar"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gunakan kabel lain dan coba lagi"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel mungkin tidak mendukung layar"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kabel USB-C mungkin tidak terhubung dengan benar ke layar"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-is-watch/strings.xml b/core/res/res/values-is-watch/strings.xml
index 50781a821521..a3e7bfe75e08 100644
--- a/core/res/res/values-is-watch/strings.xml
+++ b/core/res/res/values-is-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Skynjarar"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Neyðartilkynning"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Undirbúningur fyrir uppfærslu"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Kerfisuppfærsla Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Veldu innslátt"</string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index f666308bb537..f5ad24b04053 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Það getur fylgst með samskiptum þínum við forrit eða skynjara vélbúnaðar og haft samskipti við forrit fyrir þína hönd."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Leyfa"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Hafna"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ýttu á eiginleika til að byrja að nota hann:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Veldu eiginleika sem á að nota með aðgengishnappinum"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Veldu eiginleika sem á að nota með flýtileið hljóðstyrkstakka"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helgi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Viðburður"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Svefn"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Innra vandamál kom upp í tækinu og það kann að vera óstöðugt þangað til þú núllstillir það."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Innra vandamál kom upp í tækinu. Hafðu samband við framleiðanda til að fá nánari upplýsingar."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Lokað er fyrir hljóðnemann"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ekki er hægt að spegla á skjá"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Notaðu aðra snúru og reyndu aftur"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Ekki er víst að snúran styðji skjái"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Ekki er víst að USB-C-snúran tengist skjám á réttan hátt"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Tveir skjáir"</string>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index e01c55ff8788..1b19a880f50e 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS emergenze"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparazione dell\'aggiornamento in corso…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Aggiornamento di sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Scegli input"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9ffadbe08fd1..80262d2cc597 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Può tenere traccia delle tue interazioni con un\'app o un sensore hardware e interagire con app per tuo conto."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Consenti"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Rifiuta"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocca una funzionalità per iniziare a usarla:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Scegli le funzionalità da usare con il pulsante Accessibilità"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Scegli le funzionalità da usare con la scorciatoia dei tasti del volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fine settimana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Notte"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Si è verificato un problema interno con il dispositivo, che potrebbe essere instabile fino al ripristino dei dati di fabbrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Si è verificato un problema interno con il dispositivo. Per informazioni dettagliate, contatta il produttore."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microfono bloccato"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Impossibile eseguire il mirroring al display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Usa un altro cavo e riprova"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Il cavo potrebbe non supportare i display"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Il cavo USB-C potrebbe non collegarsi correttamente ai display"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Doppio schermo"</string>
diff --git a/core/res/res/values-iw-watch/strings.xml b/core/res/res/values-iw-watch/strings.xml
index 3dc8a86a494d..4a10c9606e84 100644
--- a/core/res/res/values-iw-watch/strings.xml
+++ b/core/res/res/values-iw-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"חיישנים"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"מצב חירום"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"בהכנה לתהליך העדכון"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"‏עדכון מערכת של Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"בחירת קלט"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 53ec382af0f1..59dc3b27e8e1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"אפשרות למעקב אחר האינטראקציה שלך עם אפליקציות או חיישני חומרה, וביצוע אינטראקציה בשמך."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"אישור"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"עדיף שלא"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"יש להקיש על תכונה כדי להתחיל להשתמש בה:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"בחירת תכונה לשימוש עם לחצן הנגישות"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"בחירת תכונות לשימוש עם מקש הקיצור לעוצמת הקול"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"סוף השבוע"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"אירוע"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"שינה"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"חלק מהצלילים מושתקים על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"קיימת בעיה פנימית במכשיר שלך, וייתכן שהוא לא יתפקד כראוי עד שיבוצע איפוס לנתוני היצרן."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"קיימת בעיה פנימית במכשיר שלך. לקבלת פרטים, יש ליצור קשר עם היצרן."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"המיקרופון חסום"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"לא ניתן לשקף למסך"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"צריך להשתמש בכבל שונה ולנסות שוב"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"יכול להיות שהכבל לא תומך במסכים"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"‏יכול להיות שכבל ה-USB-C לא יתחבר למסכים כמו שצריך"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"מצב שני מסכים"</string>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index 7e1984383423..b7251b866ff9 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"センサー"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"緊急 SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"更新を準備しています"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS システム アップデート"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"入力の選択"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 961a8da87e4a..2ac444bdaea9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1689,7 +1689,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"削除"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"このまま大音量で聴き続けますか?\n\nおすすめの時間よりも長い時間にわたってヘッドフォンの音量が大きいため、聴力を損なうおそれがあります"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"このまま大音量で聴き続けますか?\n\n推奨時間よりも長くヘッドフォンが大音量に設定されており、聴力を損なうおそれがあります"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"大きな音が検知されました\n\nヘッドフォンの音量がおすすめの音量よりも大きいため、聴力を損なうおそれがあります"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"アプリやハードウェア センサーの操作を記録したり、自動的にアプリを操作したりできます。"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"許可"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"許可しない"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"使用を開始する機能をタップ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ユーザー補助機能ボタンで使用する機能の選択"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"音量ボタンのショートカットで使用する機能の選択"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"予定"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠中"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> により一部の音はミュートに設定"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"デバイスで内部的な問題が発生しました。データが初期化されるまで不安定になる可能性があります。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"デバイスで内部的な問題が発生しました。詳しくはメーカーにお問い合わせください。"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"マイクがブロックされています"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ディスプレイにミラーリングできません"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"別のケーブルでもう一度お試しください"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ケーブルはディスプレイに対応していない可能性があります"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C ケーブルがディスプレイに正しく接続されていない可能性があります"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"デュアル スクリーン"</string>
diff --git a/core/res/res/values-ka-watch/strings.xml b/core/res/res/values-ka-watch/strings.xml
index 993d6d228512..edb8c259bd41 100644
--- a/core/res/res/values-ka-watch/strings.xml
+++ b/core/res/res/values-ka-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"სენსორები"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"გადაუდებელი დახმარება"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"მზადება განახლებისთვის"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS სისტემის განახლება"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"აირჩიეთ შემავალი სიგნალი"</string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 27f596bf7390..8da54ce39653 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"მას შეუძლია თვალი მიადევნოს თქვენს ინტერაქციებს აპის ან აპარატურის სენსორის საშუალებით, ასევე, თქვენი სახელით აწარმოოს აპებთან ინტერაქცია."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"დაშვება"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"უარყოფა"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"შეეხეთ ფუნქციას მისი გამოყენების დასაწყებად:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ მარტივი წვდომის ღილაკით"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ ხმის ღილაკის მალსახმობით"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"შაბათ-კვირა"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"მოვლენა"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ძილისას"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ფიქსირდება თქვენი მოწყობილობის შიდა პრობლემა. დეტალებისათვის, მიმართეთ თქვენს მწარმოებელს."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"მიკროფონი დაბლოკილია"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ეკრანზე არეკვლა შეუძლებელია"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"გამოიყენეთ სხვა კაბელი და ცადეთ ხელახლა"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"კაბელს შეიძლება არ ჰქონდეს ეკრანების მხარდაჭერა"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"თქვენი USB-C კაბელი შეიძლება სათანადოდ არ უკავშირდებოდეს ეკრანებს"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"ორმაგი ეკრანი"</string>
diff --git a/core/res/res/values-kk-watch/strings.xml b/core/res/res/values-kk-watch/strings.xml
index 622350c21697..9d34a0337396 100644
--- a/core/res/res/values-kk-watch/strings.xml
+++ b/core/res/res/values-kk-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Сенсорлар"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Құтқару қызметін шақыру"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Жаңартуға дайындалып жатыр"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS жүйесін жаңарту"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Енгізу әдісін таңдау"</string>
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1faea616398e..935fa75c6291 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1690,7 +1690,7 @@
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Жою"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
<string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Жоғары дыбыс деңгейінде тыңдай бересіз бе?\n\nҚұлақаспаптың жоғары дыбыс деңгейі ұсынылған уақыттан ұзақ қосылып тұрды. Есту мүшеңізге зияны тиюі мүмкін."</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Қатты дыбыс анықталды\n\nҚұлақаспаптың жоғары дыбыс деңгейі ұсынылған уақыттан ұзақ қосылып тұрды. Есту мүшеңізге зияны тиюі мүмкін."</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Қатты дыбыс анықталды\n\nҚұлақаспаптың дыбысы ұсынылған деңгейден асып кетті. Есту мүшеңізге зияны тиюі мүмкін."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Арнайы мүмкіндіктердің жылдам пәрмені іске қосылсын ба?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ол қолданбамен немесе жабдық датчигімен істеген тапсырмаларыңызды бақылайды және қолданбаларды сіздің атыңыздан пайдаланады."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Рұқсат ету"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Тыйым салу"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны пайдалана бастау үшін түртіңіз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Демалыс күндері"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Іс-шара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ұйқы режимі"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон блокталған."</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Дисплейге көшірмені көрсету мүмкін емес"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Басқа кабельмен әрекетті қайталап көріңіз."</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабель дисплейлерді қолдамауы мүмкін"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C кабелі дисплейлерге дұрыс жалғанбаған болуы мүмкін."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-km-watch/strings.xml b/core/res/res/values-km-watch/strings.xml
index ae7ba7d288f3..4a13b7a1f045 100644
--- a/core/res/res/values-km-watch/strings.xml
+++ b/core/res/res/values-km-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ឧបករណ៍ចាប់សញ្ញា"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS ពេលមានអាសន្ន"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"កំពុងរៀបចំធ្វើបច្ចុប្បន្នភាព"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"បច្ចុប្បន្នភាព​ប្រព័ន្ធ Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ជ្រើសរើសវិធីបញ្ចូល"</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 470c39ed2ee6..95d6ca165628 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"វា​អាចតាមដានអន្តរកម្មរបស់អ្នកជាមួយនឹងកម្មវិធី ឬសេនស័រហាតវែរ និងធ្វើអន្តរកម្ម​ជាមួយកម្មវិធីនានា​ជំនួសឱ្យអ្នក។"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"អនុញ្ញាត"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"បដិសេធ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ចុចមុខងារណាមួយ ដើម្បចាប់ផ្ដើមប្រើ៖"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ជ្រើសរើស​មុខងារ ដើម្បីប្រើ​ជាមួយ​ប៊ូតុង​ភាពងាយស្រួល"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ជ្រើសរើស​មុខងារ ដើម្បីប្រើ​ជាមួយផ្លូវកាត់គ្រាប់ចុច​កម្រិតសំឡេង"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ចុងសប្ដាហ៍"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ព្រឹត្តិការណ៍"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"កំពុងដេក"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> កំពុង​បិទសំឡេង​មួយចំនួន"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ហើយវាអ្នកមិនមានស្ថេរភាព រហូតទាល់តែអ្នកកំណត់ដូចដើមវិញទាំងស្រុង។"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ទំនាក់ទំនងក្រុមហ៊ុនផលិតឧបករណ៍របស់អ្នកសម្រាប់ព័ត៌មានបន្ថែម។"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"មីក្រូហ្វូនត្រូវ​បានទប់ស្កាត់"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"មិនអាចបញ្ចាំងទៅផ្ទាំងអេក្រង់បានទេ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ប្រើខ្សែផ្សេង រួចព្យាយាមម្តងទៀត"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ខ្សែប្រហែលជាមិនអាចប្រើជាមួយផ្ទាំងអេក្រង់បានទេ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ខ្សែ USB-C របស់អ្នក​ប្រហែលជា​មិនអាចភ្ជាប់​ផ្ទាំងអេក្រង់​បានត្រឹមត្រូវទេ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"អេក្រង់ពីរ"</string>
diff --git a/core/res/res/values-kn-watch/strings.xml b/core/res/res/values-kn-watch/strings.xml
index 6b7af1816f74..e1085bd96868 100644
--- a/core/res/res/values-kn-watch/strings.xml
+++ b/core/res/res/values-kn-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ಸೆನ್ಸರ್‌ಗಳು"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ತುರ್ತು SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ಇನ್‌ಪುಟ್ ಆಯ್ಕೆಮಾಡಿ"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e2f24f69122e..b6bef49256af 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ತೆಗೆದುಹಾಕು"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ವಾಲ್ಯೂಮ್‌ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡಬೇಕೆ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್‌ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್‌ನಲ್ಲಿ ಆಲಿಸುವುದನ್ನು ಮುಂದುವರಿಸಬೇಕೇ?\n\nಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್ ಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಿನ ಸಮಯದವರೆಗೆ ಅಧಿಕವಾಗಿದ್ದು, ಇದರಿಂದ ನಿಮ್ಮ ಶ್ರವಣ ಶಕ್ತಿಗೆ ಹಾನಿಯಾಗಬಹುದು"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ದೊಡ್ಡ ಧ್ವನಿ ಪತ್ತೆಯಾಗಿದೆ\n\nಹೆಡ್‌ಫೋನ್ ವಾಲ್ಯೂಮ್ ಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಾಗಿದ್ದು, ಇದರಿಂದ ನಿಮ್ಮ ಶ್ರವಣ ಶಕ್ತಿಗೆ ಹಾನಿಯಾಗಬಹುದು"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್‌ನಲ್ಲಿ ಆಲಿಸುವುದನ್ನು ಮುಂದುವರಿಸಬೇಕೇ?\n\nಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ದೀರ್ಘಕಾಲ ಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್ ಹೆಚ್ಚಿಗೆ ಇದ್ದು, ಇದರಿಂದ ನಿಮ್ಮ ಶ್ರವಣ ಶಕ್ತಿಗೆ ಹಾನಿಯಾಗಬಹುದು"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ದೊಡ್ಡ ಧ್ವನಿ ಪತ್ತೆಯಾಗಿದೆ\n\nಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ದೀರ್ಘಕಾಲ ಹೆಡ್‌ಫೋನ್ ವಾಲ್ಯೂಮ್ ಹೆಚ್ಚಿಗೆ ಇದ್ದು, ಇದರಿಂದ ನಿಮ್ಮ ಶ್ರವಣ ಶಕ್ತಿಗೆ ಹಾನಿಯಾಗಬಹುದು"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಶಾರ್ಟ್‌ಕಟ್ ಬಳಸುವುದೇ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ಶಾರ್ಟ್‌ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್‌ಗಳನ್ನು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಿದರೆ ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ವೈಶಿಷ್ಟ್ಯವೊಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ವೈಶಿಷ್ಟ್ಯಗಳಿಗಾಗಿ ಶಾರ್ಟ್‌ಕಟ್ ಆನ್ ಮಾಡಬೇಕೇ?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ಇದು ಆ್ಯಪ್ ಅಥವಾ ಹಾರ್ಡ್‌ವೇರ್ ಸೆನ್ಸರ್‌ನ ಜೊತೆಗಿನ ನಿಮ್ಮ ಸಂವಹನಗಳನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಬಹುದು, ಮತ್ತು ನಿಮ್ಮ ಪರವಾಗಿ ಆ್ಯಪ್‌ಗಳ ಜೊತೆ ಸಂವಹನ ನಡೆಸಬಹುದು."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ಅನುಮತಿಸಿ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ನಿರಾಕರಿಸಿ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ವೈಶಿಷ್ಟ್ದ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ವಾಲ್ಯೂಮ್ ಕೀ ಶಾರ್ಟ್‌ಕಟ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ವಾರಾಂತ್ಯ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ಈವೆಂಟ್"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ನಿದ್ರೆಯ ಸಮಯ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ರೀಸೆಟ್ ಮಾಡುವವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ. ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ತಯಾರಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ಡಿಸ್‌ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ಬೇರೆ ಕೇಬಲ್ ಬಳಸಿ ಹಾಗೂ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ಡಿಸ್‌ಪ್ಲೇಗಳನ್ನು ಕೇಬಲ್ ಬೆಂಬಲಿಸದಿರಬಹುದು"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ನಿಮ್ಮ USB-C ಕೇಬಲ್ ಡಿಸ್‌ಪ್ಲೇಗಳಿಗೆ ಸರಿಯಾಗಿ ಕನೆಕ್ಟ್ ಆಗದಿರಬಹುದು"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ko-watch/strings.xml b/core/res/res/values-ko-watch/strings.xml
index c5bf06930523..4ad17504be5b 100644
--- a/core/res/res/values-ko-watch/strings.xml
+++ b/core/res/res/values-ko-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"센서"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"긴급 SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"업데이트 준비 중"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS 시스템 업데이트"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"입력 선택"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b867fe6729f2..612bc7111378 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1689,7 +1689,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"삭제"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"계속해서 높은 볼륨으로 들으시겠습니까?\n\n헤드폰 볼륨이 권장 시간보다 오랫동안 높은 상태였으며 이로 인해 청력 손상이 발생할 수 있습니다."</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"계속해서 높은 볼륨으로 들으시겠습니까?\n\n헤드폰 볼륨이 권장 시간보다 오래 높은 상태였으며 이로 인해 청력 손상이 발생할 수 있습니다."</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"큰 소리가 감지됨\n\n헤드폰 볼륨이 권장 수준보다 높으며 이로 인해 청력 손상이 발생할 수 있습니다."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다."</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"앱 또는 하드웨어 센서와의 상호작용을 추적할 수 있으며 나를 대신해 앱과 상호작용할 수 있습니다."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"허용"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"거부"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"기능을 사용하려면 탭하세요"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"접근성 버튼으로 사용할 기능 선택"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"볼륨 키 단축키로 사용할 기능 선택"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"주말"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"캘린더 일정"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"수면 시간"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"사용 중인 기기 내부에 문제가 발생했습니다. 자세한 내용은 제조업체에 문의하세요."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"마이크가 차단됨"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"디스플레이에 미러링할 수 없음"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"다른 케이블을 사용하여 다시 시도해 보세요."</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"디스플레이를 지원하지 않는 케이블일 수 있음"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"사용 중인 USB-C 케이블이 디스플레이에 제대로 연결되지 않을 수 있습니다."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ky-watch/strings.xml b/core/res/res/values-ky-watch/strings.xml
index df2b762b0f26..000b5be76afb 100644
--- a/core/res/res/values-ky-watch/strings.xml
+++ b/core/res/res/values-ky-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Сенсорлор"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Кырсыктаганда чалуу"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Жаңырганы жатат"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS системасын жаңыртуу"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Киргизүү жолун тандоо"</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 853221ee31ef..ab92325140b8 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Өчүрүү"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Үнүн катуу кылып уга бересизби?\n\nГарнитуранын үнүн катуу чыгарып, сунушталган убакыттан узагыраак угуп жатасыз. Этияттаңыз, кулагыңыздын угуусу начарлап кетиши мүмкүн"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Үнүн катуу кылып угуп жатасыз\n\nГарнитуранын үнүн катуу чыгарып, сунушталган убакыттан узагыраак угуп жатасыз. Этияттаңыз, кулагыңыздын угуусу начарлап кетиши мүмкүн"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Үнүн катуу кылып уга бересизби?\n\nГарнитуранын үнүн катуу чыгарып, сунушталгандан узагыраак угуп жатасыз. Этияттаңыз, кулагыңыздын угуусу начарлап кетиши мүмкүн"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Үнүн катуу кылып угуп жатасыз\n\nГарнитуранын үнүн сунушталгандан катуураак кылып угуп жатасыз. Этияттаңыз, кулагыңыздын угуусу начарлап кетиши мүмкүн"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Атайын мүмкүнчүлүктөрдүн ыкчам баскычын иштетесизби?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Кызмат колдонмодо жасаган аракеттериңизге же түзмөктүн сенсорлоруна көз салып, сиздин атыңыздан буйруктарды берет."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Ооба"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Жок"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны колдонуп баштоо үчүн аны таптап коюңуз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Атайын мүмкүнчүлүктөр баскычы менен колдонгуңуз келген функцияларды тандаңыз"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Үндү катуулатуу/акырындатуу баскычтары менен кайсы функцияларды иштеткиңиз келет?"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Дем алыш"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Иш-чара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Уйку режими"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> айрым үндөрдү өчүрүүдө"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Түзмөгүңүздө ички көйгөй бар жана ал баштапкы абалга кайтарылмайынча туруктуу иштебей коюшу мүмкүн."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Түзмөгүңүздө ички көйгөй бар. Анын чоо-жайын билүү үчүн өндүрүүчүңүзгө кайрылыңыз."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон бөгөттөлгөн"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Экранга күзгүдөй чагылдыруу мүмкүн эмес"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Башка кабелди колдонуп, кайра аракет кылыңыз"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабель дисплейлерди колдоого албашы мүмкүн"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C кабели дисплейлерге туура туташпашы мүмкүн"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Кош экран"</string>
diff --git a/core/res/res/values-lo-watch/strings.xml b/core/res/res/values-lo-watch/strings.xml
index d4804d328624..90d7f8e8bbdc 100644
--- a/core/res/res/values-lo-watch/strings.xml
+++ b/core/res/res/values-lo-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ເຊັນເຊີ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS ສຸກເສີນ"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"ກຳລັງກະກຽມເພື່ອອັບເດດ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"ອັບເດດລະບົບ Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ເລືອກການປ້ອນຂໍ້ມູນ"</string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index c743fc0b7b23..c4f74d04f834 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ມັນສາມາດຕິດຕາມການໂຕ້ຕອບຂອງທ່ານກັບແອັບ ຫຼື ເຊັນເຊີຮາດແວໃດໜຶ່ງ ແລະ ໂຕ້ຕອບກັບແອັບໃນນາມຂອງທ່ານໄດ້."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ອະນຸຍາດ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ປະຕິເສດ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ແຕະໃສ່ຄຸນສົມບັດໃດໜຶ່ງເພື່ອເລີ່ມການນຳໃຊ້ມັນ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບປຸ່ມການຊ່ວຍເຂົ້າເຖິງ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທາງລັດປຸ່ມລະດັບສຽງ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ທ້າຍອາທິດ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ການນັດໝາຍ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ການນອນ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ປິດສຽງບາງຢ່າງໄວ້"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ມີ​ບັນ​ຫາ​ພາຍ​ໃນ​ກັບ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ, ແລະ​ມັນ​ອາດ​ຈະ​ບໍ່​ສະ​ຖຽນ​ຈົນ​ກວ່າ​ທ່ານ​ຕັ້ງ​ເປັນ​ຂໍ້​ມູນ​ໂຮງ​ງານ​ຄືນ​ແລ້ວ."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ມີ​ບັນ​ຫາ​ພາຍ​ໃນ​ກັບ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ. ຕິດ​ຕໍ່ຜູ້​ຜະ​ລິດ​ຂອງ​ທ່ານ​ສຳ​ລັບ​ລາຍ​ລະ​ອຽດ​ຕ່າງໆ."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ໄມໂຄຣໂຟນຖືກບລັອກໄວ້"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ບໍ່ສາມາດສະທ້ອນໄປຫາຈໍສະແດງຜົນໄດ້"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ກະລຸນາໃຊ້ສາຍອື່ນແລ້ວລອງໃໝ່"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ສາຍອາດບໍ່ຮອງຮັບຈໍສະແດງຜົນ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ສາຍ USB-C ຂອງທ່ານອາດບໍ່ໄດ້ເຊື່ອມຕໍ່ກັບຈໍສະແດງຜົນຢ່າງຖືກຕ້ອງ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"ໜ້າຈໍຄູ່"</string>
diff --git a/core/res/res/values-lt-watch/strings.xml b/core/res/res/values-lt-watch/strings.xml
index ba40587f245e..668d06279ccf 100644
--- a/core/res/res/values-lt-watch/strings.xml
+++ b/core/res/res/values-lt-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Jutikliai"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Pagalbos iškv. kr. atv."</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Ruošiamasi atnaujinti"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"„Wear OS“ sistemos naujinys"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Įvesties pasirinkimas"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ff665c5bbf8e..96303beb19c9 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Naudojant šią funkciją galima stebėti jūsų sąveiką su programa ar aparatinės įrangos jutikliu ir sąveikauti su programomis jūsų vardu."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Leisti"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Atmesti"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Norėdami naudoti funkciją, palieskite ją:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funkcijų, kurioms bus naudojamas pritaikomumo mygtukas, pasirinkimas"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funkcijų, kurioms bus naudojamas garsumo spartusis klavišas, pasirinkimas"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Savaitgalį"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Įvykis"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Miegas"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Iškilo vidinė su jūsų įrenginiu susijusi problema, todėl įrenginys gali veikti nestabiliai, kol neatkursite gamyklinių duomenų."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Iškilo vidinė su jūsų įrenginiu susijusi problema. Jei reikia išsamios informacijos, susisiekite su gamintoju."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofonas užblokuotas"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Negalima bendrinti ekrano vaizdo ekrane"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Naudokite kitą laiką ir bandykite dar kartą"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Laidas gali nepalaikyti ekranų"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Gali būti, kad USB-C laidu nepavyksta tinkamai prisijungti prie ekranų"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-lv-watch/strings.xml b/core/res/res/values-lv-watch/strings.xml
index 77817e98438d..8a2ee2c271f5 100644
--- a/core/res/res/values-lv-watch/strings.xml
+++ b/core/res/res/values-lv-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Ārkārtas zvans"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Notiek gatavošanās atjaunināšanai…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS sistēmas atjauninājums"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Izvēlieties ievades metodi"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 4d369aaeb8d7..1aa526ca31cd 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tā var izsekot jūsu mijiedarbību ar lietotni vai aparatūras sensoru un mijiedarboties ar lietotnēm jūsu vārdā."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Atļaut"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Neatļaut"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Pieskarieties funkcijai, lai sāktu to izmantot"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izvēlieties funkcijas, ko izmantot ar pieejamības pogu"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izvēlieties funkcijas, ko izmantot ar skaļuma pogu saīsni"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nedēļas nogalē"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Pasākums"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Gulēšana"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaņas"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Jūsu ierīcē ir radusies iekšēja problēma, un ierīce var darboties nestabili. Lai to labotu, veiciet rūpnīcas datu atiestatīšanu."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Jūsu ierīcē ir radusies iekšēja problēma. Lai iegūtu plašāku informāciju, lūdzu, sazinieties ar ražotāju."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofons ir bloķēts."</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nevar spoguļot displeju"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Izmantojiet citu vadu un mēģiniet vēlreiz."</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Iespējams, vads neatbalsta displejus"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Iespējams, jūsu USB-C vads nevarēs nodrošināt pareizu savienojumu ar displejiem."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen režīms"</string>
diff --git a/core/res/res/values-mk-watch/strings.xml b/core/res/res/values-mk-watch/strings.xml
index b2e4218949c9..e00cf2ef8218 100644
--- a/core/res/res/values-mk-watch/strings.xml
+++ b/core/res/res/values-mk-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Сензори"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Итна помош"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Се подготвува за ажурирање"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Системско ажурирање на Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Изберете метод за внесување"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 29c9de8368d1..79c663d3f696 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Може да ја следи вашата интеракција со апликациите или со хардверските сензори и да врши интеракција со апликациите во ваше име."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дозволи"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Одбиј"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Допрете на функција за да почнете да ја користите:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Изберете ги функциите што ќе ги користите со копчето за пристапност"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Изберете ги функциите што ќе ги користите со кратенката за копчето за јачина на звук"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Настан"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спиење"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> исклучи некои звуци"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Настана внатрешен проблем со уредот и може да биде нестабилен сè додека не ресетирате на фабричките податоци."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Настана внатрешен проблем со уредот. Контактирајте го производителот за детали."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофонот е блокиран"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се отсликува за прикажување"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Користете друг кабел и обидете се повторно"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабелот можеби не поддржува екрани"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Кабелот USB-C можеби нема да се поврзе правилно со екраните"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ml-watch/strings.xml b/core/res/res/values-ml-watch/strings.xml
index e543254892a6..c71e8378ce94 100644
--- a/core/res/res/values-ml-watch/strings.xml
+++ b/core/res/res/values-ml-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"സെൻസറുകൾ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"എമർജൻസി SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"അപ്‌ഡേറ്റ് ചെയ്യാൻ തയ്യാറെടുക്കുന്നു"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS സിസ്റ്റം അപ്‌ഡേറ്റ്"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ഇൻപുട്ട് തിരഞ്ഞെടുക്കുക"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 46587ee13086..e281426d04af 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ഇതിന് ഒരു ആപ്പുമായോ ഹാർഡ്‌വെയർ സെൻസറുമായോ ഉള്ള നിങ്ങളുടെ ആശയവിനിമയങ്ങൾ ട്രാക്ക് ചെയ്യാനും നിങ്ങളുടെ പേരിൽ ആശയവിനിമയം നടത്താനും കഴിയും."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"അനുവദിക്കൂ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"നിരസിക്കുക"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ഉപയോഗിച്ച് തുടങ്ങാൻ ഫീച്ചർ ടാപ്പ് ചെയ്യുക:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ഉപയോഗസഹായി ബട്ടണിന്റെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"വോളിയം കീ കുറുക്കുവഴിയിലൂടെ ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"വാരാന്ത്യം"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ഇവന്റ്"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ഉറക്കം"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ചില ശബ്‌ദങ്ങൾ മ്യൂട്ട് ചെയ്യുന്നു"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്‌നമുണ്ട്, ഫാക്‌ടറി വിവര പുനഃസജ്ജീകരണം ചെയ്യുന്നതുവരെ ഇതു അസ്ഥിരമായിരിക്കാനിടയുണ്ട്."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്‌നമുണ്ട്. വിശദാംശങ്ങൾക്കായി നിർമ്മാതാവിനെ ബന്ധപ്പെടുക."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"മൈക്രോഫോൺ ബ്ലോക്ക് ചെയ്‌തു"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ഡിസ്‌പ്ലേയിലേക്ക് മിറർ ചെയ്യാനാകില്ല"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"മറ്റൊരു കേബിൾ ഉപയോഗിച്ച് വീണ്ടും ശ്രമിക്കുക"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"കേബിൾ, ഡിസ്പ്ലേകളെ പിന്തുണച്ചേക്കില്ല"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"നിങ്ങളുടെ USB-C കേബിൾ, ഡിസ്‌പ്ലേകളിലേക്ക് ശരിയായി കണക്റ്റ് ആയേക്കില്ല"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"ഡ്യുവൽ സ്ക്രീൻ"</string>
diff --git a/core/res/res/values-mn-watch/strings.xml b/core/res/res/values-mn-watch/strings.xml
index d01f17425ed9..1e3ae918f543 100644
--- a/core/res/res/values-mn-watch/strings.xml
+++ b/core/res/res/values-mn-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Мэдрэгч"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Яаралтай тусламж"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Шинэчлэхэд бэлтгэж байна"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-н систем шинэчлэлт"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Оролт сонгох"</string>
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 62a162e75540..6615ac7457dc 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Энэ нь таны апп болон техник хангамжийн мэдрэгчтэй хийх харилцан үйлдлийг хянах болон таны өмнөөс апптай харилцан үйлдэл хийх боломжтой."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Зөвшөөрөх"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Татгалзах"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Үүнийг ашиглаж эхлэхийн тулд онцлог дээр товшино уу:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Хандалтын товчлуурын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дууны түвшний түлхүүрийн товчлолын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Амралтын өдөр"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Үйл явдал"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Унтлагын цаг"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> зарим дууны дууг хааж байна"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Таны төхөөрөмжид дотоод алдаа байна. Дэлгэрэнгүй мэдээлэл авахыг хүсвэл үйлдвэрлэгчтэйгээ холбоо барина уу."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофоныг блоклосон байна"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Дэлгэцэд тусгал үүсгэх боломжгүй"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Өөр кабель ашиглаад, дахин оролдоно уу"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабель нь дэлгэцүүдийг дэмждэггүй байж магадгүй"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Таны USB-C кабель дэлгэцүүдэд зохих ёсоор холбогдохгүй байж магадгүй"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-mr-watch/strings.xml b/core/res/res/values-mr-watch/strings.xml
index 60925592bb75..3a6b00715be9 100644
--- a/core/res/res/values-mr-watch/strings.xml
+++ b/core/res/res/values-mr-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"सेन्सर"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"आणीबाणी SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"अपडेट करण्याची तयारी करत आहे"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS संबंधित सिस्टीम अपडेट"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"इनपुट निवडा"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 87227eee7079..6bfa0bfbf328 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"हे तुमचा ॲप किंवा हार्डवेअर सेन्सरसोबतचा परस्‍परसंवाद ट्रॅक करू शकते आणि इतर ॲप्ससोबत तुमच्या वतीने संवाद साधू शकते."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमती द्या"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"नकार द्या"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"वैशिष्ट्य वापरणे सुरू करण्यासाठी त्यावर टॅप करा:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"अ‍ॅक्सेसिबिलिटी बटणासोबत वापरायची असलेली ॲप्स निवडा"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"व्‍हॉल्‍यूम की शॉर्टकटसोबत वापरायची असलेली ॲप्स निवडा"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"आठवड्याच्या शेवटी"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इव्‍हेंट"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"झोपताना"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्‍वनी म्‍यूट करत आहे"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे आणि तुमचा फॅक्‍टरी डेटा रीसेट होईपर्यंत ती अस्‍थिर असू शकते."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे. तपशीलांसाठी आपल्‍या निर्मात्याशी संपर्क साधा."</string>
@@ -2336,6 +2344,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"मायक्रोफोन ब्लॉक केलेला आहे"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"डिस्प्लेवर मिरर करू शकत नाही"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"वेगळी केबल वापरून पुन्हा प्रयत्न करा"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"तुमचे डिव्हाइस खूप गरम आहे आणि ते थंड होईपर्यंत डिस्प्लेमध्ये मिरर करू शकत नाही"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"केबल कदाचित डिस्प्लेना सपोर्ट करणार नाही"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"तुमची USB-C केबल कदाचित डिस्प्लेना योग्यरीत्या कनेक्ट होणार नाही"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-ms-watch/strings.xml b/core/res/res/values-ms-watch/strings.xml
index b223c032e67b..97da22724b50 100644
--- a/core/res/res/values-ms-watch/strings.xml
+++ b/core/res/res/values-ms-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Penderia"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS Kecemasan"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Bersedia untuk pengemaskinian"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Kemaskinian sistem Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Pilih input"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 7794150ea86e..769f0bddf55d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ciri ini boleh menjejaki interaksi anda dengan apl atau penderia perkakasan dan berinteraksi dengan apl bagi pihak anda."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Benarkan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tolak"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketik ciri untuk mula menggunakan ciri itu:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih ciri untuk digunakan dengan butang kebolehaksesan"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih ciri untuk digunakan dengan pintasan kekunci kelantangan"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hujung minggu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Terdapat masalah dalaman dengan peranti anda. Peranti mungkin tidak stabil sehingga anda membuat tetapan semula data kilang."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Terdapat masalah dalaman dengan peranti anda. Hubungi pengilang untuk mengetahui butirannya."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon disekat"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Tidak dapat menyegerakkan kepada paparan"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gunakan kabel lain dan cuba lagi"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel mungkin tidak menyokong paparan"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kabel USB-C anda mungkin tidak bersambung kepada paparan dengan betul"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dwiskrin"</string>
diff --git a/core/res/res/values-my-watch/strings.xml b/core/res/res/values-my-watch/strings.xml
index b0f1809020b6..fd41f0bc90a5 100644
--- a/core/res/res/values-my-watch/strings.xml
+++ b/core/res/res/values-my-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"အာရုံခံကိရိယာများ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"အရေးပေါ် SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"အပ်ဒိတ်လုပ်ရန် ပြင်ဆင်နေသည်"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS စနစ် အပ်ဒိတ်လုပ်ခြင်း"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"လက်ကွက် ရွေးပါ"</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 3aaaf8bd94bf..df94876358ed 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ဖယ်ရှားရန်"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"အသံကျယ်ကျယ်ဖြင့် ဆက်နားဆင်မလား။\n\nနားကြပ်အသံအား အကြံပြုထားသည်ထက် ပိုကြာရှည်စွာ ချဲ့ထားပြီး ၎င်းက သင့်အကြားအာရုံကို ထိခိုက်စေနိုင်သည်"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ကျယ်လောင်သောအသံကို သိရှိသည်\n\nနားကြပ်အသံအား အကြံပြုထားသည်ထက် ပိုချဲ့ထားပြီး ၎င်းက သင့်အကြားအာရုံကို ထိခိုက်စေနိုင်သည်"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"အသံကျယ်ကျယ်ဖြင့် ဆက်နားဆင်မလား။\n\nနားကြပ်အသံသည် အကြံပြုထားသည်ထက် အချိန်ကြာရှည်စွာ ကျယ်လောင်နေပြီး ၎င်းက သင့်အကြားအာရုံကို ထိခိုက်စေနိုင်သည်"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ကျယ်လောင်သောအသံကို သိရှိသည်\n\nနားကြပ်အသံသည် အကြံပြုထားသည်ထက် ပိုကျယ်နေပြီး ၎င်းက သင့်အကြားအာရုံကို ထိခိုက်စေနိုင်သည်"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးနိုင်သည့် ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများအတွက် ဖြတ်လမ်းကို ဖွင့်မလား။"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"၎င်းသည် သင်နှင့် အက်ပ်တစ်ခု (သို့) အာရုံခံကိရိယာအကြား ပြန်လှန်တုံ့ပြန်မှုများကို မှတ်သားနိုင်ပြီး သင့်ကိုယ်စား အက်ပ်များနှင့် ပြန်လှန်တုံ့ပြန်နိုင်သည်။"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ခွင့်ပြုရန်"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ပယ်ရန်"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ဝန်ဆောင်မှုကို စတင်အသုံးပြုရန် တို့ပါ−"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"အများသုံးနိုင်မှု ခလုတ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"အသံခလုတ် ဖြတ်လမ်းလင့်ခ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"စနေ၊ တနင်္ဂနွေ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"အစီအစဉ်"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"အိပ်နေချိန်"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သည် အချို့အသံကို ပိတ်နေသည်"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေပြီး၊ မူလစက်ရုံထုတ်အခြေအနေအဖြစ် ပြန်လည်ရယူနိုင်သည်အထိ အခြေအနေမတည်ငြိမ်နိုင်ပါ။"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေ၏။ အသေးစိတ်သိရန်အတွက် ပစ္စည်းထုတ်လုပ်သူအား ဆက်သွယ်ပါ။"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"မိုက်ခရိုဖုန်း ပိတ်ထားသည်"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ဖန်သားပြင်တွင် စကရင်ပွား၍ မရပါ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"အခြားကေဘယ်ကြိုးသုံးပြီး ထပ်စမ်းကြည့်ပါ"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ကေဘယ်ကြိုးက ဖန်သားပြင်များကို မပံ့ပိုးခြင်း ဖြစ်နိုင်သည်"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"သင့် USB-C ကေဘယ်ကြိုးသည် ဖန်သားပြင်များနှင့် မှန်ကန်စွာ ချိတ်ဆက်မထားခြင်း ဖြစ်နိုင်သည်"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-nb-watch/strings.xml b/core/res/res/values-nb-watch/strings.xml
index 60389765e1fc..f3eafcab1f27 100644
--- a/core/res/res/values-nb-watch/strings.xml
+++ b/core/res/res/values-nb-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorer"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS-alarm"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Forbereder oppdateringen"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-systemoppdatering"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Velg inndatametode"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 9d6e8050de50..4aca6fc6fa64 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Den kan spore kommunikasjonen din med en app eller maskinvaresensor og kommunisere med apper på dine vegne."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Tillat"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Avvis"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trykk på en funksjon for å begynne å bruke den:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Velg funksjonene du vil bruke med Tilgjengelighet-knappen"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Velg funksjonene du vil bruke med volumtastsnarveien"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helg"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Aktivitet"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Det har oppstått et internt problem på enheten din, og den kan være ustabil til du tilbakestiller den til fabrikkdata."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Det har oppstått et internt problem på enheten din. Ta kontakt med produsenten for mer informasjon."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofonen er blokkert"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kan ikke speile til skjermen"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Bruk en annen kabel og prøv igjen"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabelen støtter kanskje ikke skjermer"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C-kabelen din kobler seg kanskje ikke til skjermer på riktig måte"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ne-watch/strings.xml b/core/res/res/values-ne-watch/strings.xml
index 7f20abd3636d..90ca297131fa 100644
--- a/core/res/res/values-ne-watch/strings.xml
+++ b/core/res/res/values-ne-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"सेन्सरहरू"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"आपत्‌कालीन सेवामा फोन गर्ने सुविधा"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"अपडेट गर्ने तयारी गरिँदै छ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS को सिस्टम अपडेट"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"इनपुट छनौट गर्नुहोस्"</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3b7de10e0a91..16279129f730 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"हटाउनुहोस्"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ठुलो आवाजमा सुनिरहन चाहनुहुन्छ?\n\nहेडफोनको भोल्युम सिफारिस गरिएको समयभन्दा लामो समयदेखि उच्च छ। यसले तपाईंको श्रवण शक्तिमा क्षति पुर्‍याउन सक्छ"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ठुलो आवाज पत्ता लाग्यो\n\nहेडफोनको भोल्युम सिफारिस गरिएको स्तरभन्दा उच्च छ। यसले तपाईंको श्रवण शक्ति क्षय गर्न सक्छ"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ठुलो साउन्डमा सुनिरहन चाहनुहुन्छ?\n\nहेडफोनको भोल्युम धेरै बेरदेखि उच्च छ। यसले तपाईंको श्रवण शक्तिमा क्षति पुर्‍याउन सक्छ"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"साउन्ड ठूलो भयो\n\nहेडफोनको भोल्युम सिफारिस गरिएको स्तरभन्दा उच्च छ। यसले तपाईंको श्रवण शक्ति क्षय गर्न सक्छ"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"एक्सेसिबिलिटीसम्बन्धी सुविधा प्रयोग गर्न सर्टकट अन गर्ने हो?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"यसले कुनै एप वा हार्डवेयर सेन्सरसँग तपाईंले गर्ने अन्तर्क्रिया ट्र्याक गर्न सक्छ र तपाईंका तर्फबाट एपहरूसँग अन्तर्क्रिया गर्न सक्छ।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमति दिनुहोस्"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"नदिनुहोस्"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"कुनै सुविधा प्रयोग गर्न थाल्न उक्त सुविधामा ट्याप गर्नुहोस्:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"पहुँचको बटनमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"भोल्युम कुञ्जीको सर्टकटमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"शनिबार"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"कार्यक्रम"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"निदाएका बेला"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ। विवरणहरूको लागि आफ्नो निर्मातासँग सम्पर्क गर्नुहोस्।"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"माइक्रोफोन म्युट गरिएको छ"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"डिस्प्लेमा मिरर गर्न सकिएन"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"अर्कै केबल प्रयोग गरी फेरि प्रयास गर्नुहोस्"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"यो केबल डिस्प्लेहरूमा प्रयोग गर्न नमिल्न सक्छ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"तपाईंको USB-C केबल डिस्प्लेहरूमा राम्रोसँग नजोडिन सक्छ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-nl-watch/strings.xml b/core/res/res/values-nl-watch/strings.xml
index bdcaf42431ce..ece6c999733c 100644
--- a/core/res/res/values-nl-watch/strings.xml
+++ b/core/res/res/values-nl-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensoren"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Update voorbereiden"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS-systeemupdate"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Invoer kiezen"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a4bedd325af2..5ae7d6d25372 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Deze functie kan je interacties met een app of een hardwaresensor bijhouden en namens jou met apps communiceren."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Toestaan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Weigeren"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op een functie om deze te gebruiken:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Functies kiezen voor gebruik met de knop Toegankelijkheid"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraken"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microfoon is geblokkeerd"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kan niet spiegelen naar scherm"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gebruik een andere kabel en probeer het opnieuw"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"De kabel ondersteunt misschien geen schermen"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Je USB-C-kabel sluit misschien niet goed aan op schermen"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-or-watch/strings.xml b/core/res/res/values-or-watch/strings.xml
index 7fd7c19efe7b..7db0025e784b 100644
--- a/core/res/res/values-or-watch/strings.xml
+++ b/core/res/res/values-or-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ସେନ୍ସର୍‍"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ଜରୁରୀକାଳୀନ SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"ଅପଡେଟ କରିବାକୁ ପ୍ରସ୍ତୁତ କରାଯାଉଛି"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS ସିଷ୍ଟମ୍ ଅପଡେଟ୍"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ଇନପୁଟ ବାଛନ୍ତୁ"</string>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index af76df84f3d0..9997f165bde6 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ଅଧିକ ଭଲ୍ୟୁମରେ ଶୁଣିବା ଜାରି ରଖିବେ?\n\nସୁପାରିଶ କରାଯାଇଥିବା ଅପେକ୍ଷା ଅଧିକ ସମୟ ପାଇଁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି, ଯାହା ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତିକୁ ନଷ୍ଟ କରିପାରିବ"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ଉଚ୍ଚ ସାଉଣ୍ଡ ଚିହ୍ନଟ କରାଯାଇଛି\n\nହେଡଫୋନର ଭଲ୍ୟୁମ ସୁପାରିଶ କରାଯାଇଥିବା ଅପେକ୍ଷା ଅଧିକ ଅଛି, ଯାହା ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତିକୁ ନଷ୍ଟ କରିପାରିବ"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ଅଧିକ ଭଲ୍ୟୁମରେ ଶୁଣିବା ଜାରି ରଖିବେ?\n\nସୁପାରିଶ କରାଯାଇଥିବା ଭଲ୍ୟୁମ ଠାରୁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି, ଯାହା ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତିକୁ ନଷ୍ଟ କରିପାରେ"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ଲାଉଡ ସାଉଣ୍ଡ ଚିହ୍ନଟ ହୋଇଛି\n\nସୁପାରିଶ ଭଲ୍ୟୁମ ଠାରୁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି, ଯାହା ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତିକୁ ନଷ୍ଟ କରିପାରେ"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍‍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକ ପାଇଁ ସର୍ଟକଟ୍ ଚାଲୁ କରିବେ?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ଏହା କୌଣସି ଆପ କିମ୍ବା ହାର୍ଡୱେର ସେନ୍ସର ସହ ଆପଣଙ୍କର ଇଣ୍ଟେରାକ୍ସନକୁ ଟ୍ରାକ କରିପାରେ ଏବଂ ଆପଣଙ୍କ ତରଫରୁ ଆପ୍ସ ସହ ଇଣ୍ଟରାକ୍ଟ କରିପାରେ।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ଅନୁମତି"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ଅଗ୍ରାହ୍ୟ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ଏକ ଫିଚର୍ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ଏହାକୁ ଟାପ୍ କରନ୍ତୁ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ଭଲ୍ୟୁମ୍ କୀ ସର୍ଟକଟ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ସପ୍ତାହାନ୍ତ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ଇଭେଣ୍ଟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ଶୋଇବା"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> କିଛି ସାଉଣ୍ଡକୁ ମ୍ୟୁଟ୍ କରୁଛି"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ଏକ ସମସ୍ୟା ରହିଛି ଏବଂ ଆପଣ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଅସ୍ଥିର ରହିପାରେ।"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଏକ ସମସ୍ୟା ରହିଛି। ବିବରଣୀ ପାଇଁ ଆପଣଙ୍କ ଉତ୍ପାଦକଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ମାଇକ୍ରୋଫୋନକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ଡିସପ୍ଲେ କରିବାକୁ ମିରର କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ଏକ ଭିନ୍ନ କେବୁଲ ବ୍ୟବହାର କରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"କେବୁଲ ଡିସପ୍ଲେଗୁଡ଼ିକୁ ସମର୍ଥନ କରିନପାରେ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ଆପଣଙ୍କ USB-C କେବୁଲ ଡିସପ୍ଲେଗୁଡ଼ିକ ସହ ସଠିକ ଭାବରେ କନେକ୍ଟ ହୋଇନପାରେ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-pa-watch/strings.xml b/core/res/res/values-pa-watch/strings.xml
index fd31f9b8a4f3..4e0e2677411e 100644
--- a/core/res/res/values-pa-watch/strings.xml
+++ b/core/res/res/values-pa-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"ਸੰੰਵੇਦਕ"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ਐਮਰਜੈਂਸੀ ਸਹਾਇਤਾ"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"ਅੱਪਡੇਟ ਦੀ ਤਿਆਰੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS ਸਿਸਟਮ ਅੱਪਡੇਟ"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ਇਨਪੁੱਟ ਚੁਣੋ"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1db544a21b42..4b9289dfd457 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ਹਟਾਓ"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ਕੀ ਉੱਚੀ ਅਵਾਜ਼ ਵਿੱਚ ਸੁਣਨਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?\n\nਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਸਿਫ਼ਾਰਸ਼ੀ ਸਮੇਂ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਦੇਰ ਤੱਕ ਉੱਚੀ ਰੱਖੀ ਗਈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ਉੱਚੀ ਧੁਨੀ ਦਾ ਪਤਾ ਲੱਗਾ\n\nਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਨੂੰ ਸਿਫ਼ਾਰਸ਼ੀ ਪੱਧਰ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਦੇਰ ਤੱਕ ਉੱਚੀ ਰੱਖਿਆ ਗਿਆ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ਕੀ ਉੱਚੀ ਅਵਾਜ਼ ਵਿੱਚ ਸੁਣਦੇ ਰਹਿਣਾ ਹੈ?\n\nਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਸਿਫ਼ਾਰਸ਼ੀ ਸਮੇਂ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਦੇਰ ਤੱਕ ਉੱਚੀ ਰੱਖੀ ਹੋਈ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ਉੱਚੀ ਅਵਾਜ਼ ਦਾ ਪਤਾ ਲੱਗਾ\n\nਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਸਿਫ਼ਾਰਸ਼ੀ ਪੱਧਰ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਦੇਰ ਤੱਕ ਉੱਚੀ ਰੱਖੀ ਹੋਈ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਲਈ ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ਇਹ ਕਿਸੇ ਐਪ ਜਾਂ ਹਾਰਡਵੇਅਰ ਸੈਂਸਰ ਦੇ ਨਾਲ ਤੁਹਾਡੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਸਕਦੀ ਹੈ, ਅਤੇ ਤੁਹਾਡੀ ਤਰਫ਼ੋਂ ਐਪਾਂ ਦੇ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ਕਰਨ ਦਿਓ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ਨਾ ਕਰਨ ਦਿਓ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ਕਿਸੇ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਵਰਤਣਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਉਸ \'ਤੇ ਟੈਪ ਕਰੋ:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ਅਵਾਜ਼ ਕੁੰਜੀ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ਹਫ਼ਤੇ ਦਾ ਅੰਤਲਾ ਦਿਨ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ਇਵੈਂਟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ਸੌਣ ਵੇਲੇ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਕੁਝ ਧੁਨੀਆਂ ਨੂੰ ਮਿਊਟ ਕਰ ਰਹੀ ਹੈ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਹੈ ਅਤੇ ਇਹ ਅਸਥਿਰ ਹੋ ਸਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਨਹੀਂ ਕਰਦੇ।"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਸੀ। ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਨਿਰਮਾਤਾ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ਕੋਈ ਵੱਖਰੀ ਕੇਬਲ ਵਰਤ ਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੇਬਲ ਡਿਸਪਲੇਆਂ ਦਾ ਸਮਰਥਨ ਨਾ ਕਰੇ"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਤੁਹਾਡੀ USB-C ਕੇਬਲ ਡਿਸਪਲੇਆਂ ਨਾਲ ਠੀਕ ਤਰ੍ਹਾਂ ਕਨੈਕਟ ਨਾ ਹੋਵੇ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-pl-watch/strings.xml b/core/res/res/values-pl-watch/strings.xml
index 4f4b6614645a..b459c4039923 100644
--- a/core/res/res/values-pl-watch/strings.xml
+++ b/core/res/res/values-pl-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Czujniki"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Połączenie alarmowe"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Przygotowuję się do aktualizacji"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Aktualizacja systemu Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Wybierz metodę wprowadzania"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 18bea3fee54d..0273cc34a175 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1691,7 +1691,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Usuń"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Słuchać dalej z wysokim poziomem głośności?\n\nGłośność na słuchawkach jest zbyt duża przez czas dłuższy niż zalecany, co może doprowadzić do uszkodzenia słuchu"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Chcesz słuchać dalej z wysokim poziomem głośności?\n\nGłośność na słuchawkach jest zbyt duża przez czas dłuższy niż zalecany, co może doprowadzić do uszkodzenia słuchu"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Wykryto głośny dźwięk\n\nGłośność na słuchawkach przekracza zalecane wartości, co może doprowadzić do uszkodzenia słuchu"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Użyć skrótu ułatwień dostępu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Gdy skrót jest włączony, jednoczesne naciskanie przez trzy sekundy obu przycisków głośności uruchamia funkcję ułatwień dostępu."</string>
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Może śledzić Twoje interakcje z aplikacjami lub czujnikiem sprzętowym, a także obsługiwać aplikacje za Ciebie."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Zezwól"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odmów"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Wybierz funkcję, aby zacząć z niej korzystać:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Wybierz funkcje, których chcesz używać z przyciskiem ułatwień dostępu"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Wybierz funkcje, do których chcesz używać skrótu z klawiszami głośności"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Wydarzenie"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sen"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dźwięki"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"W Twoim urządzeniu wystąpił problem wewnętrzny. Może być ono niestabilne, dopóki nie przywrócisz danych fabrycznych."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"W Twoim urządzeniu wystąpił problem wewnętrzny. Skontaktuj się z jego producentem, by otrzymać szczegółowe informacje."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon jest zablokowany"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nie można utworzyć odbicia lustrzanego na wyświetlaczu"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Użyj innego kabla i spróbuj ponownie"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel może nie obsługiwać wyświetlaczy"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kabel USB-C może nie łączyć się prawidłowo z wyświetlaczami"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Podwójny ekran"</string>
diff --git a/core/res/res/values-pt-rBR-watch/strings.xml b/core/res/res/values-pt-rBR-watch/strings.xml
index e06a5471dfa7..6345cb8b44b2 100644
--- a/core/res/res/values-pt-rBR-watch/strings.xml
+++ b/core/res/res/values-pt-rBR-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS de emergência"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparando para atualizar"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Atualização do sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Escolher entrada"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 486318ddc02e..ec7658da99bf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1690,8 +1690,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remover"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Continuar ouvindo em volume alto?\n\nO volume dos fones de ouvido está alto há mais tempo que o recomendado. Isso pode causar danos à audição"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Som alto detectado\n\nO volume dos fones de ouvido está mais alto que o recomendado. Isso pode causar danos à audição"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Continuar ouvindo em volume alto?\n\nO volume dos fones de ouvido está alto há mais tempo que o recomendado. Isso pode causar danos à audição."</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Som alto detectado\n\nO volume dos fones de ouvido está mais alto que o recomendado. Isso pode causar danos à audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Ativar atalho para recursos de acessibilidade?"</string>
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pode monitorar suas interações com um app ou um sensor de hardware e interagir com apps em seu nome."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Negar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"O microfone está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Não é possível espelhar a tela"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Use outro cabo e tente de novo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Talvez o cabo não tenha suporte a telas"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Seu cabo USB-C pode não se conectar a telas corretamente"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Tela dupla"</string>
diff --git a/core/res/res/values-pt-rPT-watch/strings.xml b/core/res/res/values-pt-rPT-watch/strings.xml
index d828e37aad56..f93ab83583ea 100644
--- a/core/res/res/values-pt-rPT-watch/strings.xml
+++ b/core/res/res/values-pt-rPT-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Urgência SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"A preparar para atualizar"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Atualização do sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Escolha o método de introdução"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 784f2a9ca4d0..b834c0f1f48d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pode monitorizar as suas interações com uma app ou um sensor de hardware e interagir com apps em seu nome."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Recusar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque numa funcionalidade para começar a utilizá-la:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha funcionalidades para utilizar com o botão Acessibilidade"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha funcionalidades para usar com o atalho das teclas de volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"A dormir"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Existe um problema interno no seu dispositivo. Contacte o fabricante para obter mais informações."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"O microfone está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Não é possível espelhar para o ecrã"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Use um cabo diferente e tente novamente"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"O cabo pode não suportar ecrãs"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"O cabo USB-C pode não se ligar a ecrãs corretamente"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-pt-watch/strings.xml b/core/res/res/values-pt-watch/strings.xml
index e06a5471dfa7..6345cb8b44b2 100644
--- a/core/res/res/values-pt-watch/strings.xml
+++ b/core/res/res/values-pt-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensores"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS de emergência"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Preparando para atualizar"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Atualização do sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Escolher entrada"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 486318ddc02e..ec7658da99bf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1690,8 +1690,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remover"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Continuar ouvindo em volume alto?\n\nO volume dos fones de ouvido está alto há mais tempo que o recomendado. Isso pode causar danos à audição"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Som alto detectado\n\nO volume dos fones de ouvido está mais alto que o recomendado. Isso pode causar danos à audição"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Continuar ouvindo em volume alto?\n\nO volume dos fones de ouvido está alto há mais tempo que o recomendado. Isso pode causar danos à audição."</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Som alto detectado\n\nO volume dos fones de ouvido está mais alto que o recomendado. Isso pode causar danos à audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Ativar atalho para recursos de acessibilidade?"</string>
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pode monitorar suas interações com um app ou um sensor de hardware e interagir com apps em seu nome."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Negar"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"O microfone está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Não é possível espelhar a tela"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Use outro cabo e tente de novo"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Talvez o cabo não tenha suporte a telas"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Seu cabo USB-C pode não se conectar a telas corretamente"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Tela dupla"</string>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index 27f926e53800..f1d2977bef35 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzori"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Apel de urgență"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Se pregătește actualizarea"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Actualizare de sistem pentru Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Alege metoda de introducere a textului"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 88f5044eefdf..dc89003efa3d 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Poate să urmărească interacțiunile tale cu o aplicație sau cu un senzor hardware și să interacționeze cu aplicații în numele tău."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permite"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuz"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atinge o funcție ca să începi să o folosești:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alege funcțiile pe care să le folosești cu butonul de accesibilitate"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alege funcțiile pentru comanda rapidă a butonului de volum"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Eveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactează producătorul."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microfonul este blocat"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nu se poate oglindi pe ecran"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Folosește alt cablu și încearcă din nou"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Cablul poate să nu fie compatibil cu ecranele"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Cablul USB-C poate să nu se conecteze corespunzător la ecrane"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-ru-watch/strings.xml b/core/res/res/values-ru-watch/strings.xml
index fc0d3b7c5cde..7aa71b8b5d65 100644
--- a/core/res/res/values-ru-watch/strings.xml
+++ b/core/res/res/values-ru-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Датчики"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Экстренные вызовы"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Подготовка к обновлению…"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Обновление Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Выберите способ ввода"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 7f5e87f2bbb7..8bd65763bbae 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Сервис может отслеживать ваше взаимодействие с приложениями и датчиками устройства и давать приложениям команды от вашего имени."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Разрешить"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Отклонить"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Выберите, какую функцию использовать:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберите функции, которые будут запускаться с помощью кнопки специальных возможностей"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберите функции, которые будут запускаться с помощью кнопки регулировки громкости"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выходные"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Мероприятие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Время сна"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Произошла внутренняя ошибка. Обратитесь к производителю устройства за подробными сведениями."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон заблокирован."</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не удается дублировать на экран"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Используйте другой кабель или повторите попытку."</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабель может не подходить для подключения к дисплеям"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Возможно, подключение дисплеев с помощью этого кабеля USB-C не поддерживается."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-si-watch/strings.xml b/core/res/res/values-si-watch/strings.xml
index 00170704783c..5ef72a5560fe 100644
--- a/core/res/res/values-si-watch/strings.xml
+++ b/core/res/res/values-si-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"සංවේදක"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"හදිසි SOS උදවු"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"යාවත්කාලීන කිරීමට සූදානම් කරමින්"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS පද්ධති යාවත්කාලීනය"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ආදානය තෝරා ගන්න"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index e90f1a2cea22..cba8d477ca3b 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"මෙයට යෙදුමක් හෝ දෘඪාංග සංවේදකයක් සමඟ ඔබේ අන්තර්ක්‍රියා හඹා යෑමට, සහ ඔබ වෙනුවෙන් යෙදුම් සමඟ අන්තර්ක්‍රියාවේ යෙදීමට හැකි ය."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ඉඩ දෙන්න"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ප්‍රතික්‍ෂේප කරන්න"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"එය භාවිත කිරීම ආරම්භ කිරීමට විශේෂාංගයක් තට්ටු කරන්න:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ප්‍රවේශ්‍යතා බොත්තම සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"හඬ පරිමා යතුරු කෙටිමග සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"සති අන්තය"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"සිදුවීම"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"නිදා ගනිමින්"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සමහර ශබ්ද නිහඬ කරමින්"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ඔබේ උපාංගය සමගින් ගැටලුවක් ඇති අතර, ඔබේ කර්මාන්තශාලා දත්ත යළි සකසන තෙක් එය අස්ථායි විය හැකිය."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"ඔබේ උපාංගය සමගින් අභ්‍යන්තර ගැටලුවක් ඇත. විස්තර සඳහා ඔබේ නිෂ්පාදක අමතන්න."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"මයික්‍රෆෝනය අවහිර කර ඇත"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"සංදර්ශකයට දර්පණය කළ නොහැක"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"වෙනස් කේබලයක් භාවිතා කර නැවත උත්සාහ කරන්න"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"කේබලය සංදර්ශක වෙත සහාය නොදැක්විය හැක"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"ඔබේ USB-C කේබලයට සංදර්ශකවලට නිසි ලෙස සම්බන්ධ නොවිය හැක"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sk-watch/strings.xml b/core/res/res/values-sk-watch/strings.xml
index 790e4240b24d..c5d81c5beb71 100644
--- a/core/res/res/values-sk-watch/strings.xml
+++ b/core/res/res/values-sk-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Senzory"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Pomoc v tiesni"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Pripravuje sa na aktualizáciu"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Aktualizácia systému Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Výber vstupu"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 3da576c4fd5a..61a88201ab05 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Môže sledovať vaše interakcie s aplikáciou alebo hardvérovým senzorom a interagovať s aplikáciami za vás."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Povoliť"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zamietnuť"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Klepnutím na funkciu ju začnite používať:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Výber funkcií, ktoré chcete používať tlačidlom dostupnosti"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Výber funkcií, ktoré chcete používať klávesovou skratkou"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Udalosť"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánok"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Vo vašom zariadení došlo k internému problému. Môže byť nestabilné, kým neobnovíte jeho výrobné nastavenia."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Vo vašom zariadení došlo k internému problému. Ak chcete získať podrobné informácie, obráťte sa na jeho výrobcu."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofón je blokovaný"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nedá sa zrkadliť do obrazovky"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Použite iný kábel a skúste znova"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kábel nemusí podporovať obrazovky"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kábel USB‑C sa nemusí dať správne pripojiť k obrazovkám"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sl-watch/strings.xml b/core/res/res/values-sl-watch/strings.xml
index 1954f298265d..0055a6d7af53 100644
--- a/core/res/res/values-sl-watch/strings.xml
+++ b/core/res/res/values-sl-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Tipala"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Nujni primer"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Pripravljanje na posodobitev"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Posodobitev sistema Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Izbira vhodne naprave"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5b731b730398..97f91f3408e4 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Spremlja lahko vaše interakcije z aplikacijo ali tipalom strojne opreme ter komunicira z aplikacijami v vašem imenu."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dovoli"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zavrni"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Če želite začeti uporabljati funkcijo, se je dotaknite:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izberite funkcije, ki jih želite uporabljati z gumbom za dostopnost"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izberite funkcije, ki jih želite uporabljati z bližnjico na tipki za glasnost"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Konec tedna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Dogodek"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spanje"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Vaša naprava ima notranjo napako in bo morda nestabilna, dokler je ne ponastavite na tovarniške nastavitve."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Vaša naprava ima notranjo napako. Če želite več informacij, se obrnite na proizvajalca."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ni mogoče zrcaliti zaslona"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Uporabite drug kabel in poskusite znova"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel morda ne podpira zaslonov"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kabel USB-C se morda ne more ustrezno povezati z zasloni."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sq-watch/strings.xml b/core/res/res/values-sq-watch/strings.xml
index a35d3973538b..802a85f6eee2 100644
--- a/core/res/res/values-sq-watch/strings.xml
+++ b/core/res/res/values-sq-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorët"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Thirrja e urgjencës"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Po përgatitet për përditësimin"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Përditësimi i sistemit Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Zgjidh hyrjen"</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index a4fd1bf18b39..ea5795d38e44 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Mund të monitorojë ndërveprimet me një aplikacion ose një sensor hardueri dhe të ndërveprojë me aplikacionet në emrin tënd."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Lejo"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuzo"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trokit te një veçori për të filluar ta përdorësh atë:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Zgjidh veçoritë që do të përdorësh me butonin e qasshmërisë"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Zgjidh veçoritë që do të përdorësh me shkurtoren e tastit të volumit"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fundjava"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ngjarje"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Në gjumë"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ka një problem të brendshëm me pajisjen tënde. Ajo mund të jetë e paqëndrueshme derisa të rivendosësh të dhënat në gjendje fabrike."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Ka një problem të brendshëm me pajisjen tënde. Kontakto prodhuesin tënd për detaje."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoni është i bllokuar"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nuk mund të pasqyrojë tek ekrani"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Përdor një kabllo tjetër dhe provo përsëri"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kablloja nuk mund të mbështetë ekranet"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Kablloja jote USB-C mund të mos lidhet siç duhet me ekranet"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sr-watch/strings.xml b/core/res/res/values-sr-watch/strings.xml
index edc4a8b926fa..74c507fd1168 100644
--- a/core/res/res/values-sr-watch/strings.xml
+++ b/core/res/res/values-sr-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Сензори"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Хитна помоћ"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Ажурирање се припрема"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Ажурирање система за Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Одаберите унос"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index e5ae2f71a226..e5bb5a973817 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1711,6 +1711,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Може да прати интеракције са апликацијом или сензором хардвера и користи апликације уместо вас."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дозволи"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Одбиј"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Додирните неку функцију да бисте почели да је користите:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Одаберите функције које ћете користити са дугметом Приступачност"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције за пречицу тастером јачине звука"</string>
@@ -1905,6 +1909,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Догађај"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спавање"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Дошло је до интерног проблема у вези са уређајем. Потражите детаље од произвођача."</string>
@@ -2337,6 +2345,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон је блокиран"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Пресликавање на екран није могуће"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Употребите други кабл и пробајте поново"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабл не подржава екране"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C кабл се не повезује правилно са екранима"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sv-watch/strings.xml b/core/res/res/values-sv-watch/strings.xml
index d3afbf6d74d3..729992d52f96 100644
--- a/core/res/res/values-sv-watch/strings.xml
+++ b/core/res/res/values-sv-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorer"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS-larm"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Förbereder uppdatering"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Systemuppdatering av Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Välj ingång"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f3917fe70d17..d80c7590444f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Den kan registrera din användning av en app eller maskinvarusensor och interagera med appar åt dig."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Tillåt"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Neka"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryck på funktioner som du vill aktivera:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Välj vilka funktioner du vill använda med hjälp av tillgänglighetsknappen"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Välj att funktioner att använda med hjälp av volymknappskortkommandot"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"I helgen"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Händelse"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"När jag sover"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ett internt problem har uppstått i enheten, och det kan hända att problemet kvarstår tills du återställer standardinställningarna."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Ett internt problem har uppstått i enheten. Kontakta tillverkaren om du vill veta mer."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofonen är blockerad"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Det går inte spegla till skärmen"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Använd en annan kabel och försök igen"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabeln kanske inte har stöd för skärmar"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Det kanske inte går att ansluta skärmar korrekt med den här USB-C-kabeln"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-sw-watch/strings.xml b/core/res/res/values-sw-watch/strings.xml
index 7c4d69701a4a..ed79d9bac202 100644
--- a/core/res/res/values-sw-watch/strings.xml
+++ b/core/res/res/values-sw-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Vihisi"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Vipengele vya Dharura"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Inajiandaa kusasisha"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Sasisho la mfumo wa Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Teua kibodi"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 95c1b25e51f7..3c2a02bc7ce7 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1689,8 +1689,8 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ondoa"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Ungependa kuendelea kusikiliza kwa sauti ya kiwango cha juu?\n\nKiwango cha sauti ya vipokea sauti vya kichwani kimekuwa juu kwa muda mrefu kuliko inavyopendekezwa, hali ambayo inaweza kuharibu uwezo wako wa kusikia"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Sauti ya kiwango cha juu imetambuliwa\n\nKiwango cha sauti ya vipokea sauti vya kichwani kimekuwa juu zaidi kuliko inavyopendekezwa, hali ambayo inaweza kuharibu uwezo wako wa kusikia"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Ungependa kuendelea kusikiliza kwa sauti ya juu?\n\nKiwango cha sauti ya vipokea sauti vya kichwani kimekuwa juu kwa muda mrefu kuliko inavyopendekezwa, hali ambayo inaweza kuharibu uwezo wako wa kusikia"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Sauti ya kiwango cha juu imetambuliwa\n\nKiwango cha sauti ya vipokea sauti vya kichwani kimekuwa juu kuliko inavyopendekezwa, hali ambayo inaweza kuharibu uwezo wako wa kusikia"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ungependa kutumia njia ya mkato ya ufikivu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa sekunde tatu itafungua kipengele cha ufikivu."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Ungependa kuwasha njia ya mkato ya vipengele vya ufikivu?"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Inaweza kufuatilia mawasiliano yako na programu au kitambuzi cha maunzi na kuwasiliana na programu zingine kwa niaba yako."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Ruhusu"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Kataa"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Gusa kipengele ili uanze kukitumia:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chagua vipengele vya kutumia na kitufe cha zana za ufikivu"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chagua vipengele vya kutumia na njia ya mkato ya kitufe cha sauti"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wikendi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tukio"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Kulala"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Kuna hitilafu ya ndani ya kifaa chako, na huenda kisiwe thabiti mpaka urejeshe mipangilio ya kiwandani."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Kuna hitilafu ya ndani ya kifaa chako. Wasiliana na mtengenezaji wa kifaa chako kwa maelezo."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Maikrofoni imezuiwa"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Imeshindwa kuakisi kwenye skrini"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Tumia kebo tofauti kisha ujaribu tena"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Huenda kebo haioani na skrini"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Huenda kebo yako ya USB-C isiunganishwe vizuri na skrini"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-ta-watch/strings.xml b/core/res/res/values-ta-watch/strings.xml
index 342e46ea3d9f..3c8af8356aef 100644
--- a/core/res/res/values-ta-watch/strings.xml
+++ b/core/res/res/values-ta-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"உணர்விகள்"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"அவசர உதவி"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"புதுப்பிக்கத் தயாராகிறது"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS சிஸ்டம் புதுப்பிப்பு"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"உள்ளீட்டு முறையைத் தேர்வுசெய்தல்"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9ad4bd2bfb72..79bc2a472108 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ஏதேனும் ஆப்ஸ் அல்லது வன்பொருள் சென்சாரின் உதவியுடன் உரையாடல்களைக் கண்காணித்து உங்கள் சார்பாக ஆப்ஸுடன் உரையாட இச்சேவையால் இயலும்."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"அனுமதி"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"நிராகரி"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ஒரு அம்சத்தைப் பயன்படுத்த அதைத் தட்டவும்:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"அணுகல்தன்மை பட்டன் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ஒலியளவு விசை ஷார்ட்கட் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"வார இறுதி"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"நிகழ்வு"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"உறக்கத்தில்"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> சில ஒலிகளை முடக்குகிறது"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது, அதனை ஆரம்பநிலைக்கு மீட்டமைக்கும் வரை நிலையற்று இயங்கலாம்."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது. விவரங்களுக்கு சாதன தயாரிப்பாளரைத் தொடர்புகொள்ளவும்."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"மைக்ரோஃபோன் முடக்கப்பட்டுள்ளது"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"டிஸ்ப்ளேயில் பிரதிபலிக்க முடியவில்லை"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"வெவ்வேறு கேபிள்களைப் பயன்படுத்தி மீண்டும் முயலவும்"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"டிஸ்ப்ளேக்களைக் கேபிள் ஆதரிக்காமல் இருக்கக்கூடும்"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"டிஸ்ப்ளேக்களில் உங்கள் USB-C கேபிள் சரியாக இணைக்கப்படாமல் இருக்கக்கூடும்"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"இரட்டைத் திரை"</string>
diff --git a/core/res/res/values-te-watch/strings.xml b/core/res/res/values-te-watch/strings.xml
index 056733fac780..b4668d117a51 100644
--- a/core/res/res/values-te-watch/strings.xml
+++ b/core/res/res/values-te-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"సెన్సార్‌లు"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"ఎమర్జెన్సీ సహాయం"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"అప్‌డేట్ చేయడానికి సిద్ధమవుతోంది"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS సిస్టమ్ అప్‌డేట్"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ఇన్‌పుట్‌ను ఎంచుకోండి"</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d1c336d863f7..a2862ba3123c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"మీరు ఒక యాప్‌‌తో చేసే ఇంటరాక్షన్‌లను లేదా హార్డ్‌వేర్ సెన్సార్‌ను ట్రాక్ చేస్తూ మీ త‌ర‌ఫున యాప్‌లతో ఇంటరాక్ట్ చేయగలదు."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"అనుమతించండి"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"వద్దు"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్‌ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"వాల్యూమ్ కీ షార్ట్‌కట్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"వారాంతం"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈవెంట్"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"నిద్రావస్థ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది. వివరాల కోసం మీ తయారీదారుని సంప్రదించండి."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"మైక్రోఫోన్ బ్లాక్ చేయబడింది"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"డిస్‌ప్లే చేయడానికి మిర్రర్ చేయడం సాధ్యపడదు"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"వేరే కేబుల్‌ను ఉపయోగించి, మళ్లీ ట్రై చేయండి"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"డిస్‌ప్లేలను కేబుల్ సపోర్ట్ చేయకపోవచ్చు"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"మీ USB-C కేబుల్, డిస్‌ప్లేలకు సరిగ్గా కనెక్ట్ కాకపోవచ్చు"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-th-watch/strings.xml b/core/res/res/values-th-watch/strings.xml
index 37fbd6370f62..29a95f8e84f0 100644
--- a/core/res/res/values-th-watch/strings.xml
+++ b/core/res/res/values-th-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"เซ็นเซอร์"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS ฉุกเฉิน"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"กำลังเตรียมอัปเดต"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"อัปเดตระบบ Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"เลือกการป้อนข้อมูล"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4f53fcddae4a..16e7611788c7 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1690,7 +1690,7 @@
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ลบ"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
<string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"ต้องการฟังเสียงดังต่อไปไหม\n\nเสียงของหูฟังอยู่ในระดับที่ดังเป็นระยะเวลานานกว่าที่แนะนำ ซึ่งอาจทำให้เกิดความเสียหายต่อระบบการได้ยินของคุณ"</string>
- <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"การตรวจจับเสียงดัง\n\nเสียงของหูฟังอยู่ในระดับที่ดังกว่าที่แนะนำ ซึ่งอาจทำให้เกิดความเสียหายต่อระบบการได้ยินของคุณ"</string>
+ <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"ตรวจพบเสียงดัง\n\nเสียงของหูฟังอยู่ในระดับที่ดังกว่าที่แนะนำ ซึ่งอาจทำให้เกิดความเสียหายต่อระบบการได้ยินของคุณ"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ใช้ทางลัดการช่วยเหลือพิเศษไหม"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มนาน 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"เปิดใช้ทางลัดสำหรับฟีเจอร์การช่วยเหลือพิเศษใช่ไหม"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"การควบคุมนี้สามารถติดตามการโต้ตอบของคุณกับแอปหรือกับเซ็นเซอร์ของฮาร์ดแวร์ และโต้ตอบกับแอปต่างๆ แทนคุณ"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"อนุญาต"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ปฏิเสธ"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"แตะฟีเจอร์เพื่อเริ่มใช้"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"เลือกฟีเจอร์ที่จะใช้กับปุ่มการช่วยเหลือพิเศษ"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"เลือกฟีเจอร์ที่จะใช้กับทางลัดปุ่มปรับระดับเสียง"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"สุดสัปดาห์"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"กิจกรรม"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"นอนหลับ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> กำลังปิดเสียงบางรายการ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง อุปกรณ์อาจทำงานไม่เสถียรจนกว่าคุณจะรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง โปรดติดต่อผู้ผลิตเพื่อขอรายละเอียดเพิ่มเติม"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"ไมโครโฟนถูกบล็อก"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"มิเรอร์ไปยังจอแสดงผลไม่ได้"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"โปรดใช้สายอื่นและลองอีกครั้ง"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"สายสัญญาณอาจไม่รองรับจอแสดงผล"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"สาย USB-C อาจเชื่อมต่อกับจอแสดงผลอย่างไม่ถูกต้อง"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-tl-watch/strings.xml b/core/res/res/values-tl-watch/strings.xml
index 2213858c85cd..4c65b53a1394 100644
--- a/core/res/res/values-tl-watch/strings.xml
+++ b/core/res/res/values-tl-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Mga Sensor"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Emergency SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Naghahandang mag-update"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Pag-update ng system ng Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Pumili ng input"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 2477698e740d..b2a7f25fa771 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Masusubaybayan nito ang iyong mga pakikipag-ugayan sa isang app o hardware na sensor, at puwede itong makipag-ugnayan sa mga app para sa iyo."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Payagan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tanggihan"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"I-tap ang isang feature para simulan itong gamitin:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pumili ng mga feature na gagana sa pamamagitan ng button ng accessibility"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pumili ng mga feature na gagana sa pamamagitan ng shortcut ng volume key"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Pag-sleep"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"May internal na problema sa iyong device, at maaaring hindi ito maging stable hanggang sa i-reset mo ang factory data."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"May internal na problema sa iyong device. Makipag-ugnayan sa iyong manufacturer upang malaman ang mga detalye."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Naka-block ang mikropono"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Hindi makapag-mirror sa display"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gumamit ng ibang cable at subukan ulit"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Posibleng hindi sinusuportahan ng cable ang mga display"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Posibleng hindi kumonekta nang maayos sa mga display ang iyong USB-C cable"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-tr-watch/strings.xml b/core/res/res/values-tr-watch/strings.xml
index 42c543dbe5ff..a282066759fe 100644
--- a/core/res/res/values-tr-watch/strings.xml
+++ b/core/res/res/values-tr-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensörler"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Acil Yardım"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Güncellemeye hazırlanıyor"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS sistem güncellemesi"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Giriş seçin"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 2ebfe91be0cd..eb58785bbf5f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1689,7 +1689,7 @@
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Kaldır"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
- <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Yüksek sesle dinlemeye devam edilsin mi?\n\nKulaklığın sesi önerilenden daha uzun süre yüksek düzeyde kaldı ve bu durum işitme kaybına neden olabilir"</string>
+ <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Yüksek sesle dinlemeye devam edilsin mi?\n\nKulaklığın sesinin önerilenden uzun süre yüksek düzeyde kalması işitme kaybına neden olabilir"</string>
<string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Yüksek ses algılandı\n\nKulaklığın ses düzeyi önerilenden yüksek. Bu durum işitme kaybına neden olabilir"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erişilebilirlik Kısayolu Kullanılsın mı?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kısayol açıkken ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır."</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Bir uygulama veya donanım sensörüyle etkileşimlerinizi takip edebilir ve sizin adınıza uygulamalarla etkileşimde bulunabilir."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İzin ver"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Reddet"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kullanmaya başlamak için bir özelliğe dokunun:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Erişilebilirlik düğmesiyle kullanılacak özellikleri seçin"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ses tuşu kısayoluyla kullanılacak özellikleri seçin"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hafta sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Etkinlik"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyku"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızla ilgili dahili bir sorun oluştu ve fabrika verilerine sıfırlama işlemi gerçekleştirilene kadar kararsız çalışabilir."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Cihazınızla ilgili dahili bir sorun oluştu. Ayrıntılı bilgi için üreticinizle iletişim kurun."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon engellenmiş"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ekrana yansıtılamıyor"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Farklı kablo kullanarak tekrar deneyin"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kablo, ekranları desteklemeyebilir"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C kablonuz ekranlara doğru şekilde bağlanamayabilir"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-uk-watch/strings.xml b/core/res/res/values-uk-watch/strings.xml
index 5bcd773baf6f..b1df5e52f483 100644
--- a/core/res/res/values-uk-watch/strings.xml
+++ b/core/res/res/values-uk-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Датчики"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Екстрені виклики"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Підготовка до оновлення"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Оновлення системи Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Виберіть метод введення"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index d4cd2071bd87..393fa761a086 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1712,6 +1712,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Цей сервіс може відстежувати вашу взаємодію з додатком чи апаратним датчиком, а також взаємодіяти з додатками від вашого імені."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дозволити"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Заборонити"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Натисніть функцію, щоб почати використовувати її:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Виберіть функції для кнопки спеціальних можливостей"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Виберіть функції для комбінації з клавішами гучності"</string>
@@ -1906,6 +1910,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"На вихідних"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Подія"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Під час сну"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає деякі звуки"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"На пристрої сталася внутрішня помилка. Зв’яжіться з виробником пристрою, щоб дізнатися більше."</string>
@@ -2338,6 +2346,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Мікрофон заблоковано"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Неможливо дублювати на дисплей"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Скористайтесь іншим кабелем і повторіть спробу"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Кабель може не підтримувати дисплеї"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Ваш кабель USB-C може не підключатися до дисплеїв належним чином"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-ur-watch/strings.xml b/core/res/res/values-ur-watch/strings.xml
index 6d6c73539451..ad37cd7dcdef 100644
--- a/core/res/res/values-ur-watch/strings.xml
+++ b/core/res/res/values-ur-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"سینسرز"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"‏ایمرجنسی SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"اپ ڈیٹ کرنے کی تیاری ہو رہی ہے"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"‏Wear OS سسٹم اپ ڈیٹ"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"ان پٹ منتخب کریں"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8634294d5a05..9ea7348ef577 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"یہ کسی ایپ یا ہارڈویئر سینسر کے ساتھ آپ کے تعاملات کو ٹریک کر سکتا ہے، اور آپ کی طرف سے ایپس کے ساتھ تعامل کر سکتا ہے۔"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"اجازت دیں"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مسترد کریں"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ایک خصوصیت کا استعمال شروع کرنے کیلئے اسے تھپتھپائیں:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ایکسیسبیلٹی بٹن کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"والیوم کلید کے شارٹ کٹ کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ویک اینڈ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ایونٹ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"سونا"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"آپ کے آلہ میں ایک داخلی مسئلہ ہے۔ تفصیلات کیلئے اپنے مینوفیکچرر سے رابطہ کریں۔"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"مائیکروفون مسدود ہے"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ڈسپلے پر دو طرفہ مطابقت پذیری ممکن نہیں ہے"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"مختلف کیبل استعمال کریں اور دوبارہ کوشش کریں"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"ہو سکتا ہے کہ کیبل ڈسپلیز کو سپورٹ نہ کرے"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"‏ہو سکتا ہے کہ آپ کی USB-C کیبل مناسب طریقے سے ڈسپلیز سے منسلک نہ ہو"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"دوہری اسکرین"</string>
diff --git a/core/res/res/values-uz-watch/strings.xml b/core/res/res/values-uz-watch/strings.xml
index 93bce9b79c2b..45297b5805ea 100644
--- a/core/res/res/values-uz-watch/strings.xml
+++ b/core/res/res/values-uz-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Sensorlar"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Favqulodda yordam"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Yangilashga tayyorlanmoqda"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS uchun tizim yangilanishi"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Kiritish uslubini tanlang"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index fdea194637ae..78f352906d1b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ilova yoki qurilma sensori bilan munosabatlaringizni kuzatishi hamda sizning nomingizdan ilovalar bilan ishlashi mumkin."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Ruxsat"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Rad etish"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kerakli funksiyani tanlang"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Qulayliklar tugmasi bilan foydalanish uchun funksiyalarni tanlang"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tovush tugmasi bilan ishga tushiriladigan funksiyalarni tanlang"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Dam olish kunlari"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tadbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyquda"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. U zavod sozlamalari tiklanmaguncha barqaror ishlamasligi mumkin."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. Tafsilotlar uchun qurilmangiz ishlab chiqaruvchisiga murojaat qiling."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon bloklandi"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Displeyga translatsiya qilinmaydi"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Boshqa kabel yordamida qayta urining"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Kabel displeylar bilan ishlamasligi mumkin"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C kabelingiz displeylarga toʻgʻri ulanmasligi mumkin"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Ikkita ekran"</string>
diff --git a/core/res/res/values-vi-watch/strings.xml b/core/res/res/values-vi-watch/strings.xml
index f4e17f3c8515..8616aca21ea2 100644
--- a/core/res/res/values-vi-watch/strings.xml
+++ b/core/res/res/values-vi-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Cảm biến"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"SOS khẩn cấp"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Đang chuẩn bị cập nhật"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Bản cập nhật hệ thống Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Chọn phương thức nhập"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 12a61edfca58..1b6ededd1d8a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Dịch vụ này có thể theo dõi các hoạt động tương tác của bạn với một ứng dụng hoặc bộ cảm biến phần cứng, cũng như có thể thay mặt bạn tương tác với các ứng dụng."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Cho phép"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Từ chối"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Nhấn vào một tính năng để bắt đầu sử dụng:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chọn các tính năng để dùng với nút hỗ trợ tiếp cận"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chọn các tính năng để dùng với phím tắt là phím âm lượng"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cuối tuần"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sự kiện"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ngủ"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang tắt một số âm thanh"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Đã xảy ra sự cố nội bộ với thiết bị của bạn và thiết bị có thể sẽ không ổn định cho tới khi bạn thiết lập lại dữ liệu ban đầu."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Đã xảy ra sự cố nội bộ với thiết bị. Hãy liên hệ với nhà sản xuất của bạn để biết chi tiết."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Micrô đang bị chặn"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Không chiếu được nội dung lên màn hình"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Hãy dùng một cáp khác rồi thử lại"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Có thể cáp không hỗ trợ màn hình"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Có thể cáp USB-C của bạn chưa kết nối đúng cách với màn hình"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
diff --git a/core/res/res/values-zh-rCN-watch/strings.xml b/core/res/res/values-zh-rCN-watch/strings.xml
index 470544d2726c..afa726e6c430 100644
--- a/core/res/res/values-zh-rCN-watch/strings.xml
+++ b/core/res/res/values-zh-rCN-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"传感器"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"紧急求救"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"正在准备更新"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS 系统更新"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"选择输入法"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2ae8fe653ded..676ab00f0464 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"此功能可以跟踪您与应用或硬件传感器的互动,并代表您与应用互动。"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"允许"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"拒绝"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"点按相应功能即可开始使用:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"选择可通过“无障碍”按钮使用的功能"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"选择可通过音量键快捷方式使用的功能"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"周末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活动"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"您的设备内部出现了问题。请联系您的设备制造商了解详情。"</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"麦克风已被屏蔽"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"无法镜像到显示屏"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"请改用其他数据线并重试"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"数据线可能不支持显示屏"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"您的 USB-C 数据线可能没有正确连接到显示屏"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"双屏幕"</string>
diff --git a/core/res/res/values-zh-rHK-watch/strings.xml b/core/res/res/values-zh-rHK-watch/strings.xml
index 456f3cff3ed3..d36fae9e0be6 100644
--- a/core/res/res/values-zh-rHK-watch/strings.xml
+++ b/core/res/res/values-zh-rHK-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"感應器"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"緊急求救"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"正在準備更新"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS 系統更新"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"選擇輸入法"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fe3644e68e5e..76dabaf688f5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1406,7 +1406,7 @@
<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>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"顯示在其他應用程式上層"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"在其他應用程式上面顯示"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"「<xliff:g id="NAME">%s</xliff:g>」目前可顯示在其他應用程式上面"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"「<xliff:g id="NAME">%s</xliff:g>」正在其他應用程式上顯示內容"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"如果你不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"這項功能會追蹤你與應用程式或硬件感應器的互動,並代表你直接與應用程式互動。"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"允許"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"拒絕"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕按即可開始使用所需功能:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要配搭無障礙功能按鈕使用的功能"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要用音量快速鍵的功能"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"你裝置的系統發生問題,請聯絡你的製造商瞭解詳情。"</string>
@@ -2336,6 +2344,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"已封鎖麥克風"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"無法將畫面鏡像投放至螢幕"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請改用其他連接線,然後再試一次"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"裝置過熱,請等到降溫後再將畫面鏡像投放至螢幕"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"連接線可能不支援顯示屏"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"你的 USB-C 連接線可能未妥善連接顯示屏"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"雙螢幕"</string>
diff --git a/core/res/res/values-zh-rTW-watch/strings.xml b/core/res/res/values-zh-rTW-watch/strings.xml
index 456f3cff3ed3..d36fae9e0be6 100644
--- a/core/res/res/values-zh-rTW-watch/strings.xml
+++ b/core/res/res/values-zh-rTW-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"感應器"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"緊急求救"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"正在準備更新"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Wear OS 系統更新"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"選擇輸入法"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5b6520168d5d..fc5f262f3ebc 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"可追蹤你與應用程式或硬體感應器的互動,並代表你與應用程式進行互動。"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"允許"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"拒絕"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕觸即可開始使用所需功能:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要搭配無障礙工具按鈕使用的功能"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要搭配音量快速鍵使用的功能"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"你的裝置發生內部問題,詳情請洽裝置製造商。"</string>
@@ -2336,6 +2344,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"麥克風已封鎖"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"無法將畫面鏡像投放至螢幕"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請改用其他傳輸線,然後再試一次"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"裝置過熱,請等到降溫後再將畫面鏡像投放至螢幕"</string>
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"傳輸線可能不支援螢幕"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"USB-C 傳輸線可能未妥善連接到螢幕"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"雙螢幕"</string>
diff --git a/core/res/res/values-zu-watch/strings.xml b/core/res/res/values-zu-watch/strings.xml
index edb0a6d37041..18b1410fbb1c 100644
--- a/core/res/res/values-zu-watch/strings.xml
+++ b/core/res/res/values-zu-watch/strings.xml
@@ -22,9 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permgrouplab_sensors" msgid="2439544173324807471">"Izinzwa"</string>
<string name="global_action_emergency" msgid="2097576936362874627">"Isimo esiphuthumayo se-SOS"</string>
- <!-- no translation found for reboot_to_update_prepare (4129802024411268230) -->
- <skip />
+ <string name="reboot_to_update_prepare" msgid="4129802024411268230">"Ilungiselela ukubuyekeza"</string>
<string name="reboot_to_update_title" msgid="8043761242418682803">"Isibuyekezo sesistimu ye-Wear OS"</string>
- <!-- no translation found for select_input_method (1285150113084396451) -->
- <skip />
+ <string name="select_input_method" msgid="1285150113084396451">"Khetha okokufaka"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index b701abe18d93..a88d832b3636 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1710,6 +1710,10 @@
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ingalandela ukusebenzisana kwakho nohlelo lokusebenza noma inzwa yehadiwe, nokusebenzisana nezinhlelo zokusebenza engxenyeni yakho."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Vumela"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Phika"</string>
+ <!-- no translation found for accessibility_dialog_button_uninstall (2952465517671708108) -->
+ <skip />
+ <!-- no translation found for accessibility_dialog_touch_filtered_warning (3741940116597822451) -->
+ <skip />
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Thepha isici ukuqala ukusisebenzisa:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Khetha izici ongazisebenzisa nenkinobho yokufinyeleleka"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Khetha izici ongazisebenzisa nesinqamuleli sokhiye wevolumu"</string>
@@ -1904,6 +1908,10 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ngempelasonto"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Umcimbi"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ulele"</string>
+ <!-- no translation found for zen_mode_implicit_activated (2634285680776672994) -->
+ <skip />
+ <!-- no translation found for zen_mode_implicit_deactivated (8688441768371501750) -->
+ <skip />
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Kukhona inkinga yangaphakathi ngedivayisi yakho, futhi ingase ibe engazinzile kuze kube yilapho usetha kabusha yonke idatha."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Kukhona inkinga yangaphakathi ngedivayisi yakho. Xhumana nomkhiqizi wakho ukuze uthole imininingwane."</string>
@@ -2336,6 +2344,8 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Imakrofoni ivinjiwe"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ayikwazi ukufanisa nesibonisi"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Sebenzisa ikhebuli ehlukile bese uyazama futhi"</string>
+ <!-- no translation found for connected_display_thermally_unavailable_notification_content (9205758199439955949) -->
+ <skip />
<string name="connected_display_cable_dont_support_displays_notification_title" msgid="8477183783847392465">"Ikhebuli ingase ingasekeli iziboniso"</string>
<string name="connected_display_cable_dont_support_displays_notification_content" msgid="8080498819171483973">"Ikhebuli yakho ye-USB-C ingase ingaxhumi kahle kuzibonisi"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Isikrini esikabili"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3496994fe173..698c5bad3801 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4303,6 +4303,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is true.-->
<attr name="requireDeviceScreenOn" format="boolean"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4327,6 +4330,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is false.-->
<attr name="requireDeviceScreenOn"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 9f99dc94bc92..f8546b73a37e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1592,6 +1592,12 @@
<!-- Whether attributions provided are meant to be user-visible. -->
<attr name="attributionsAreUserVisible" format="boolean" />
+ <!-- If a preloaded APK is marked updatableSystem = false, any request for an update will be rejected.
+ If an APK marked updatableSystem = false is being installed, regardless of the updatableSystem state
+ of the version it's replacing, the install will be rejected.
+ This is a private attribute, used without android: namespace. -->
+ <attr name="updatableSystem" format="boolean" />
+
<!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
together. -->
<attr name="foregroundServiceType">
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 30beee0d02a1..eddd81e78692 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -521,7 +521,7 @@
<color name="system_surface_dim_dark">#121316</color>
<color name="system_surface_variant_dark">#44474F</color>
<color name="system_on_surface_variant_dark">#C4C6D0</color>
- <color name="system_outline_dark">#72747D</color>
+ <color name="system_outline_dark">#8E9099</color>
<color name="system_outline_variant_dark">#444746</color>
<color name="system_error_dark">#FFB4A8</color>
<color name="system_on_error_dark">#690001</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f827d28b26fd..6cd6eb4b8df9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5465,6 +5465,7 @@
<item>1,1,1.0,.15,15</item>
<item>0,0,0.7,0,1</item>
<item>0,0,0.83333,0,1</item>
+ <item>0,0,1.1667,0,1</item>
</string-array>
<!-- The integer index of the selected option in config_udfps_touch_detection_options -->
@@ -6806,6 +6807,10 @@
<!-- Whether or not ActivityManager PSS profiling is disabled. -->
<bool name="config_am_disablePssProfiling">false</bool>
+ <!-- The modifier used to adjust AM's PSS threshold for downgrading services to service B if
+ RSS is being collected instead. -->
+ <item name="config_am_pssToRssThresholdModifier" format="float" type="dimen">1.5</item>
+
<!-- Whether unlocking and waking a device are sequenced -->
<bool name="config_orderUnlockAndWake">false</bool>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 764279490643..8d80af41680a 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -205,6 +205,13 @@
<string name="config_satellite_emergency_handover_intent_action" translatable="false"></string>
<java-symbol type="string" name="config_satellite_emergency_handover_intent_action" />
+ <!-- Whether outgoing satellite datagrams should be sent to modem in demo mode. When satellite
+ is enabled for demo mode, if this config is enabled, outgoing datagrams will be sent to
+ modem; otherwise, success results will be returned. If demo mode is disabled, outgoing
+ datagrams are always sent to modem. -->
+ <bool name="config_send_satellite_datagram_to_modem_in_demo_mode">false</bool>
+ <java-symbol type="bool" name="config_send_satellite_datagram_to_modem_in_demo_mode" />
+
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
supported. The network will be torn down on the source transport, and will be
@@ -236,10 +243,6 @@
<bool name="allow_clear_initial_attach_data_profile">false</bool>
<java-symbol type="bool" name="allow_clear_initial_attach_data_profile" />
- <!-- Boolean indicating whether TelephonyAnalytics module is active or not. -->
- <bool name="telephony_analytics_switch">true</bool>
- <java-symbol type="bool" name="telephony_analytics_switch" />
-
<!-- Whether to enable modem on boot if behavior is not defined -->
<bool name="config_enable_cellular_on_boot_default">true</bool>
<java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a2a4e34f3527..eed186ad3702 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4701,6 +4701,13 @@
<string name="accessibility_dialog_button_allow">Allow</string>
<!-- String for the deny button in accessibility permission dialog. [CHAR LIMIT=10] -->
<string name="accessibility_dialog_button_deny">Deny</string>
+ <!-- String for the uninstall button in accessibility permission dialog. -->
+ <string name="accessibility_dialog_button_uninstall">Uninstall</string>
+ <!-- Warning shown when user input has been blocked due to another app overlaying screen
+ content. Since we don't know what the app is showing on top of the input target, we
+ can't verify user consent. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_dialog_touch_filtered_warning">An app is obscuring the permission
+ request so your response cannot be verified.</string>
<!-- Title for accessibility select shortcut menu dialog. [CHAR LIMIT=100] -->
<string name="accessibility_select_shortcut_menu_title">Tap a feature to start using it:</string>
@@ -5245,6 +5252,11 @@
<!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
<string name="zen_mode_default_every_night_name">Sleeping</string>
+ <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+ <string name="zen_mode_implicit_activated">On</string>
+ <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+ <string name="zen_mode_implicit_deactivated">Off</string>
+
<!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
<string name="muted_by"><xliff:g id="third_party">%1$s</xliff:g> is muting some sounds</string>
@@ -6297,6 +6309,8 @@ ul.</string>
<string name="connected_display_unavailable_notification_title">Can\'t mirror to display</string>
<!-- Content of connected display unavailable notification. [CHAR LIMIT=NONE] -->
<string name="connected_display_unavailable_notification_content">Use a different cable and try again</string>
+ <!-- Content of connected display unavailable due to thermals notification. [CHAR LIMIT=NONE] -->
+ <string name="connected_display_thermally_unavailable_notification_content">Your device is too warm and can\'t mirror to the display until it cools down</string>
<!-- Title of cable don't support displays notifications. [CHAR LIMIT=NONE] -->
<string name="connected_display_cable_dont_support_displays_notification_title">Cable may not support displays</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 13d04e53b508..619ec31e37bc 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1560,4 +1560,87 @@ please see styles_device_defaults.xml.
<!-- The default style for input method switch dialog -->
<style name="InputMethodSwitchDialogStyle" parent="AlertDialog.DeviceDefault">
</style>
+
+ <style name="AccessibilityDialog">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:divider">@*android:drawable/list_divider_material</item>
+ <item name="android:showDividers">middle</item>
+ </style>
+
+ <style name="AccessibilityDialogServiceIcon">
+ <item name="android:layout_width">36dp</item>
+ <item name="android:layout_height">36dp</item>
+ <item name="android:layout_marginTop">16dp</item>
+ <item name="android:layout_marginBottom">16dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ </style>
+
+ <style name="AccessibilityDialogIcon">
+ <item name="android:layout_width">18dp</item>
+ <item name="android:layout_height">18dp</item>
+ <item name="android:layout_marginEnd">12dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ </style>
+
+ <style name="AccessibilityDialogTitle"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textSize">20sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ </style>
+
+ <style name="AccessibilityDialogDescription"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginTop">16dp</item>
+ <item name="android:layout_marginBottom">32dp</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="AccessibilityDialogPermissionTitle"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="AccessibilityDialogPermissionDescription"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="AccessibilityDialogButtonBarSpace">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">0dp</item>
+ <item name="android:visibility">gone</item>
+ </style>
+
+ <style name="AccessibilityDialogButtonList">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:divider">@*android:drawable/list_divider_material</item>
+ <item name="android:showDividers">middle</item>
+ </style>
+
+ <style name="AccessibilityDialogButton"
+ parent="@*android:style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2f3b51012693..24b39bc1225e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2006,6 +2006,7 @@
<java-symbol type="drawable" name="vpn_connected" />
<java-symbol type="drawable" name="vpn_disconnected" />
<java-symbol type="drawable" name="usb_cable_unknown_issue" />
+ <java-symbol type="drawable" name="ic_thermostat_notification" />
<java-symbol type="id" name="ask_checkbox" />
<java-symbol type="id" name="compat_checkbox" />
<java-symbol type="id" name="original_app_icon" />
@@ -2573,6 +2574,8 @@
<java-symbol type="string" name="zen_mode_default_weekends_name" />
<java-symbol type="string" name="zen_mode_default_events_name" />
<java-symbol type="string" name="zen_mode_default_every_night_name" />
+ <java-symbol type="string" name="zen_mode_implicit_activated" />
+ <java-symbol type="string" name="zen_mode_implicit_deactivated" />
<java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
<java-symbol type="string" name="display_rotation_camera_compat_toast_in_multi_window" />
<java-symbol type="array" name="config_system_condition_providers" />
@@ -3619,11 +3622,14 @@
<java-symbol type="string" name="accessibility_uncheck_legacy_item_warning" />
<java-symbol type="layout" name="accessibility_enable_service_warning" />
+ <java-symbol type="layout" name="accessibility_service_warning" />
<java-symbol type="id" name="accessibility_permissionDialog_icon" />
<java-symbol type="id" name="accessibility_permissionDialog_title" />
<java-symbol type="id" name="accessibility_permission_enable_allow_button" />
<java-symbol type="id" name="accessibility_permission_enable_deny_button" />
+ <java-symbol type="id" name="accessibility_permission_enable_uninstall_button" />
<java-symbol type="string" name="accessibility_enable_service_title" />
+ <java-symbol type="string" name="accessibility_dialog_touch_filtered_warning" />
<java-symbol type="layout" name="accessibility_shortcut_chooser_item" />
<java-symbol type="id" name="accessibility_shortcut_target_checkbox" />
@@ -3652,6 +3658,7 @@
<java-symbol type="drawable" name="ic_accessibility_color_inversion" />
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
+ <java-symbol type="drawable" name="ic_accessibility_generic" />
<java-symbol type="drawable" name="ic_accessibility_hearing_aid" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
@@ -5085,6 +5092,7 @@
<java-symbol type="array" name="device_state_notification_power_save_contents"/>
<java-symbol type="string" name="connected_display_unavailable_notification_title"/>
<java-symbol type="string" name="connected_display_unavailable_notification_content"/>
+ <java-symbol type="string" name="connected_display_thermally_unavailable_notification_content"/>
<java-symbol type="string" name="connected_display_cable_dont_support_displays_notification_title"/>
<java-symbol type="string" name="connected_display_cable_dont_support_displays_notification_content"/>
<java-symbol type="string" name="concurrent_display_notification_name"/>
@@ -5262,6 +5270,7 @@
<!-- For ActivityManager PSS profiling configurability -->
<java-symbol type="bool" name="config_am_disablePssProfiling" />
+ <java-symbol type="dimen" name="config_am_pssToRssThresholdModifier" />
<java-symbol type="raw" name="default_ringtone_vibration_effect" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 709646b00e5c..3a2e50aa06e8 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -155,7 +155,7 @@
<shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
<!-- Israel: 4 digits, known premium codes listed -->
- <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477" />
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
@@ -198,9 +198,6 @@
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
- <!-- Namibia: 5 digits -->
- <shortcode country="na" pattern="\\d{1,5}" free="40005" />
-
<!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 20d8d91761e7..62d58b65e62a 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -165,6 +165,7 @@
<!-- AccessibilityShortcutChooserActivityTest permissions -->
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
<application
android:theme="@style/Theme"
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 36e122301ba2..4b02257978d2 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -227,8 +227,7 @@ public class ActivityThreadTest {
try {
// Send process level config change.
ClientTransaction transaction = newTransaction(activityThread);
- transaction.addCallback(ConfigurationChangeItem.obtain(
- new Configuration(newConfig), DEVICE_ID_INVALID));
+ transaction.addCallback(ConfigurationChangeItem.obtain(newConfig, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -245,7 +244,7 @@ public class ActivityThreadTest {
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
transaction.addCallback(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), new Configuration(newConfig)));
+ activity.getActivityToken(), newConfig));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 4bbde0cd6366..723c0812468c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import android.annotation.NonNull;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
@@ -80,74 +81,29 @@ public class ObjectPoolTests {
@Test
public void testRecycleActivityConfigurationChangeItem() {
- ActivityConfigurationChangeItem emptyItem = ActivityConfigurationChangeItem.obtain(
- null /* activityToken */, Configuration.EMPTY);
- ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityConfigurationChangeItem item2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config()));
}
@Test
public void testRecycleActivityResultItem() {
- ActivityResultItem emptyItem = ActivityResultItem.obtain(
- null /* activityToken */, null /* resultInfoList */);
- ActivityResultItem item = ActivityResultItem.obtain(mActivityToken, resultInfoList());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityResultItem item2 = ActivityResultItem.obtain(mActivityToken, resultInfoList());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityResultItem.obtain(mActivityToken, resultInfoList()));
}
@Test
public void testRecycleConfigurationChangeItem() {
- ConfigurationChangeItem emptyItem = ConfigurationChangeItem.obtain(null, 0);
- ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config(), 1);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ConfigurationChangeItem item2 = ConfigurationChangeItem.obtain(config(), 1);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ConfigurationChangeItem.obtain(config(), 1));
}
@Test
public void testRecycleDestroyActivityItem() {
- DestroyActivityItem emptyItem = DestroyActivityItem.obtain(
- null /* activityToken */, false, 0);
- DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true, 117);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- DestroyActivityItem item2 = DestroyActivityItem.obtain(mActivityToken, true, 14);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true, 117));
}
@Test
public void testRecycleLaunchActivityItem() {
final IBinder activityToken = new Binder();
final Intent intent = new Intent("action");
- int ident = 57;
+ final int ident = 57;
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
activityInfo.setMaxAspectRatio(2.4f);
@@ -158,20 +114,19 @@ public class ObjectPoolTests {
final Configuration overrideConfig = new Configuration();
overrideConfig.assetsSeq = 5;
final String referrer = "referrer";
- int procState = 4;
+ final int procState = 4;
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
final IBinder assistToken = new Binder();
final IBinder shareableActivityToken = new Binder();
- int deviceId = 3;
+ final int deviceId = 3;
+ final IBinder taskFragmentToken = new Binder();
- final Supplier<LaunchActivityItem> itemSupplier = () -> new LaunchActivityItemBuilder()
- .setActivityToken(activityToken)
- .setIntent(intent)
+ testRecycle(() -> new LaunchActivityItemBuilder(
+ activityToken, intent, activityInfo)
.setIdent(ident)
- .setInfo(activityInfo)
.setCurConfig(config())
.setOverrideConfig(overrideConfig)
.setReferrer(referrer)
@@ -183,154 +138,74 @@ public class ObjectPoolTests {
.setIsForward(true)
.setAssistToken(assistToken)
.setShareableActivityToken(shareableActivityToken)
- .setTaskFragmentToken(new Binder())
+ .setTaskFragmentToken(taskFragmentToken)
.setDeviceId(deviceId)
- .build();
-
- LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
- LaunchActivityItem item = itemSupplier.get();
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- LaunchActivityItem item2 = itemSupplier.get();
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ .build());
}
@Test
public void testRecycleActivityRelaunchItem() {
- ActivityRelaunchItem emptyItem = ActivityRelaunchItem.obtain(
- null /* activityToken */, null, null, 0, null, false);
- Configuration overrideConfig = new Configuration();
- overrideConfig.assetsSeq = 5;
- ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
- referrerIntentList(), 42, mergedConfig(), true);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityRelaunchItem item2 = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
- referrerIntentList(), 42, mergedConfig(), true);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken,
+ resultInfoList(), referrerIntentList(), 42, mergedConfig(), true));
}
@Test
public void testRecycleMoveToDisplayItem() {
- MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(
- null /* activityToken */, 0, Configuration.EMPTY);
- MoveToDisplayItem item = MoveToDisplayItem.obtain(mActivityToken, 4, config());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- MoveToDisplayItem item2 = MoveToDisplayItem.obtain(mActivityToken, 3, config());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> MoveToDisplayItem.obtain(mActivityToken, 4, config()));
}
@Test
public void testRecycleNewIntentItem() {
- NewIntentItem emptyItem = NewIntentItem.obtain(
- null /* activityToken */, null /* intents */, false /* resume */);
- NewIntentItem item = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- NewIntentItem item2 = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> NewIntentItem.obtain(mActivityToken, referrerIntentList(), false));
}
@Test
public void testRecyclePauseActivityItemItem() {
- PauseActivityItem emptyItem = PauseActivityItem.obtain(
- null /* activityToken */, false, false, 0, false, false);
- PauseActivityItem item = PauseActivityItem.obtain(
- mActivityToken, true, true, 5, true, true);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- PauseActivityItem item2 = PauseActivityItem.obtain(
- mActivityToken, true, false, 5, true, true);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, 5, true, true));
}
@Test
public void testRecycleResumeActivityItem() {
- ResumeActivityItem emptyItem = ResumeActivityItem.obtain(
- null /* activityToken */, false, false);
- ResumeActivityItem item = ResumeActivityItem.obtain(mActivityToken, 3, true, false);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ResumeActivityItem item2 = ResumeActivityItem.obtain(mActivityToken, 2, true, false);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ResumeActivityItem.obtain(mActivityToken, 3, true, false));
}
@Test
public void testRecycleStartActivityItem() {
- StartActivityItem emptyItem = StartActivityItem.obtain(
- null /* activityToken */, null /* activityOptions */);
- StartActivityItem item = StartActivityItem.obtain(mActivityToken,
- ActivityOptions.makeBasic());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- StartActivityItem item2 = StartActivityItem.obtain(mActivityToken,
- ActivityOptions.makeBasic().setLaunchDisplayId(10));
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> StartActivityItem.obtain(mActivityToken, ActivityOptions.makeBasic()));
}
@Test
public void testRecycleStopItem() {
- StopActivityItem emptyItem = StopActivityItem.obtain(null /* activityToken */, 0);
- StopActivityItem item = StopActivityItem.obtain(mActivityToken, 4);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- StopActivityItem item2 = StopActivityItem.obtain(mActivityToken, 3);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> StopActivityItem.obtain(mActivityToken, 4));
}
@Test
public void testRecycleClientTransaction() {
- ClientTransaction emptyItem = ClientTransaction.obtain(null);
- ClientTransaction item = ClientTransaction.obtain(mApplicationThread);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
+ testRecycle(() -> ClientTransaction.obtain(mApplicationThread));
+ }
+ private void testRecycle(@NonNull Supplier<? extends ObjectPoolItem> obtain) {
+ // Reuse the same object after recycle.
+ final ObjectPoolItem item = obtain.get();
item.recycle();
- assertEquals(item, emptyItem);
+ final ObjectPoolItem item2 = obtain.get();
- ClientTransaction item2 = ClientTransaction.obtain(mApplicationThread);
assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+
+ // Create new object when the pool is empty.
+ final ObjectPoolItem item3 = obtain.get();
+
+ assertNotSame(item, item3);
+ assertEquals(item, item3);
+
+ // Reset fields after recycle.
+ item.recycle();
+
+ assertNotEquals(item, item3);
+
+ // Recycled objects are equal.
+ item3.recycle();
+
+ assertEquals(item, item3);
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 5a88bad37d5f..c0e2a4993e1c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -18,6 +18,8 @@ package android.app.servertransaction;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -92,17 +94,18 @@ class TestUtils {
}
static class LaunchActivityItemBuilder {
- @Nullable
- private IBinder mActivityToken;
- @Nullable
- private Intent mIntent;
+ @NonNull
+ private final IBinder mActivityToken;
+ @NonNull
+ private final Intent mIntent;
+ @NonNull
+ private final ActivityInfo mInfo;
+ @NonNull
+ private final Configuration mCurConfig = new Configuration();
+ @NonNull
+ private final Configuration mOverrideConfig = new Configuration();
+
private int mIdent;
- @Nullable
- private ActivityInfo mInfo;
- @Nullable
- private Configuration mCurConfig;
- @Nullable
- private Configuration mOverrideConfig;
private int mDeviceId;
@Nullable
private String mReferrer;
@@ -130,16 +133,11 @@ class TestUtils {
@Nullable
private IBinder mTaskFragmentToken;
- @NonNull
- LaunchActivityItemBuilder setActivityToken(@Nullable IBinder activityToken) {
- mActivityToken = activityToken;
- return this;
- }
-
- @NonNull
- LaunchActivityItemBuilder setIntent(@Nullable Intent intent) {
- mIntent = intent;
- return this;
+ LaunchActivityItemBuilder(@NonNull IBinder activityToken, @NonNull Intent intent,
+ @NonNull ActivityInfo info) {
+ mActivityToken = requireNonNull(activityToken);
+ mIntent = requireNonNull(intent);
+ mInfo = requireNonNull(info);
}
@NonNull
@@ -149,20 +147,14 @@ class TestUtils {
}
@NonNull
- LaunchActivityItemBuilder setInfo(@Nullable ActivityInfo info) {
- mInfo = info;
- return this;
- }
-
- @NonNull
- LaunchActivityItemBuilder setCurConfig(@Nullable Configuration curConfig) {
- mCurConfig = curConfig;
+ LaunchActivityItemBuilder setCurConfig(@NonNull Configuration curConfig) {
+ mCurConfig.setTo(curConfig);
return this;
}
@NonNull
- LaunchActivityItemBuilder setOverrideConfig(@Nullable Configuration overrideConfig) {
- mOverrideConfig = overrideConfig;
+ LaunchActivityItemBuilder setOverrideConfig(@NonNull Configuration overrideConfig) {
+ mOverrideConfig.setTo(overrideConfig);
return this;
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index f2b0f2e622b8..2315a58eb487 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -44,6 +45,8 @@ import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -57,6 +60,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
@@ -78,20 +83,32 @@ import java.util.stream.Collectors;
@Presubmit
public class TransactionExecutorTests {
+ @Mock
+ private ClientTransactionHandler mTransactionHandler;
+ @Mock
+ private ActivityLifecycleItem mActivityLifecycleItem;
+ @Mock
+ private IBinder mActivityToken;
+ @Mock
+ private Activity mActivity;
+
private TransactionExecutor mExecutor;
private TransactionExecutorHelper mExecutorHelper;
- private ClientTransactionHandler mTransactionHandler;
private ActivityClientRecord mClientRecord;
@Before
public void setUp() throws Exception {
- mTransactionHandler = mock(ClientTransactionHandler.class);
+ MockitoAnnotations.initMocks(this);
mClientRecord = new ActivityClientRecord();
when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
mExecutor = spy(new TransactionExecutor(mTransactionHandler));
mExecutorHelper = new TransactionExecutorHelper();
+
+ doReturn(true).when(mActivityLifecycleItem).isActivityLifecycleItem();
+ doReturn(mActivityToken).when(mActivityLifecycleItem).getActivityToken();
+ doReturn(mActivity).when(mTransactionHandler).getActivity(mActivityToken);
}
@Test
@@ -227,23 +244,21 @@ public class TransactionExecutorTests {
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
- ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- IBinder token = mock(IBinder.class);
- when(stateRequest.getActivityToken()).thenReturn(token);
- when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
- transaction.setLifecycleStateRequest(stateRequest);
+ transaction.setLifecycleStateRequest(mActivityLifecycleItem);
transaction.preExecute(mTransactionHandler);
mExecutor.execute(transaction);
- InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
+ InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
+ mActivityLifecycleItem);
inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
- inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
+ any());
}
@Test
@@ -252,23 +267,21 @@ public class TransactionExecutorTests {
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
- ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- IBinder token = mock(IBinder.class);
- when(stateRequest.getActivityToken()).thenReturn(token);
- when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(callback1);
transaction.addTransactionItem(callback2);
- transaction.addTransactionItem(stateRequest);
+ transaction.addTransactionItem(mActivityLifecycleItem);
transaction.preExecute(mTransactionHandler);
mExecutor.execute(transaction);
- InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
+ InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
+ mActivityLifecycleItem);
inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
- inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
+ any());
}
@Test
@@ -290,7 +303,7 @@ public class TransactionExecutorTests {
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
+ spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
launchTransaction.addCallback(launchItem);
mExecutor.execute(launchTransaction);
@@ -322,7 +335,7 @@ public class TransactionExecutorTests {
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
+ spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
launchTransaction.addTransactionItem(launchItem);
mExecutor.execute(launchTransaction);
@@ -534,42 +547,36 @@ public class TransactionExecutorTests {
@Test
public void testActivityItemExecute() {
- final IBinder token = mock(IBinder.class);
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- when(activityItem.getActivityToken()).thenReturn(token);
+ when(activityItem.getActivityToken()).thenReturn(mActivityToken);
transaction.addCallback(activityItem);
- final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- transaction.setLifecycleStateRequest(stateRequest);
- when(stateRequest.getActivityToken()).thenReturn(token);
- when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
+ transaction.setLifecycleStateRequest(mActivityLifecycleItem);
mExecutor.execute(transaction);
- final InOrder inOrder = inOrder(activityItem, stateRequest);
+ final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
- inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
+ any());
}
@Test
public void testExecuteTransactionItems_activityItemExecute() {
- final IBinder token = mock(IBinder.class);
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- when(activityItem.getActivityToken()).thenReturn(token);
+ when(activityItem.getActivityToken()).thenReturn(mActivityToken);
transaction.addTransactionItem(activityItem);
- final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- transaction.addTransactionItem(stateRequest);
- when(stateRequest.getActivityToken()).thenReturn(token);
- when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
+ transaction.addTransactionItem(mActivityLifecycleItem);
mExecutor.execute(transaction);
- final InOrder inOrder = inOrder(activityItem, stateRequest);
+ final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
- inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
+ any());
}
private static int[] shuffledArray(int[] inputArray) {
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 4aa62c503a41..07921bfc34f5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -173,11 +173,9 @@ public class TransactionParcelTests {
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
- final LaunchActivityItem item = new LaunchActivityItemBuilder()
- .setActivityToken(activityToken)
- .setIntent(intent)
+ final LaunchActivityItem item = new LaunchActivityItemBuilder(
+ activityToken, intent, activityInfo)
.setIdent(ident)
- .setInfo(activityInfo)
.setCurConfig(config())
.setOverrideConfig(overrideConfig)
.setReferrer(referrer)
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
index c00eb91d752a..4d45daf3570c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
@@ -52,16 +52,18 @@ public class WindowStateResizeItemTest {
private PendingTransactionActions mPendingActions;
@Mock
private IWindow mWindow;
- @Mock
+
+ private InsetsState mInsetsState;
private ClientWindowFrames mFrames;
- @Mock
private MergedConfiguration mConfiguration;
- @Mock
- private InsetsState mInsetsState;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+
+ mInsetsState = new InsetsState();
+ mFrames = new ClientWindowFrames();
+ mConfiguration = new MergedConfiguration();
}
@Test
diff --git a/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
new file mode 100644
index 000000000000..e19c4b15d300
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link android.content.pm.LauncherActivityInfo}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LauncherActivityInfoTest {
+
+ @Test
+ public void testTrimStart() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trimStart("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trimStart("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimStart("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
+ assertThat(LauncherActivityInfo.trimStart("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trimStart(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimStart(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
+ }
+
+ @Test
+ public void testTrimEnd() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trimEnd("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trimEnd("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimEnd("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimEnd("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trimEnd(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimEnd(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
+ }
+
+ @Test
+ public void testTrim() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trim("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trim("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trim("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trim("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trim("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trim("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trim(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trim(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ }
+}
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
index 42629ba41287..612562eb22dc 100644
--- a/core/tests/coretests/src/android/service/notification/ConditionTest.java
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -16,17 +16,22 @@
package android.service.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;
+import android.app.Flags;
import android.net.Uri;
import android.os.Parcel;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.common.base.Strings;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,8 +42,11 @@ import java.lang.reflect.Field;
public class ConditionTest {
private static final String CLASS = "android.service.notification.Condition";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
- public void testLongFields_inConstructors() {
+ public void testLongFields_inConstructors_classic() {
String longString = Strings.repeat("A", 65536);
Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
@@ -59,7 +67,7 @@ public class ConditionTest {
}
@Test
- public void testLongFields_viaParcel() {
+ public void testLongFields_viaParcel_classic() {
// Set fields via reflection to force them to be long, then parcel and unparcel to make sure
// it gets truncated upon unparcelling.
Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
@@ -98,4 +106,92 @@ public class ConditionTest {
assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length());
assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length());
}
+
+ @Test
+ public void testLongFields_inConstructors() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+
+ // Confirm strings are truncated via short constructor
+ Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE,
+ Condition.SOURCE_CONTEXT);
+
+ assertThat(cond1.id.toString()).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(cond1.summary).hasLength(Condition.MAX_STRING_LENGTH);
+
+ // Confirm strings are truncated via long constructor
+ Condition cond2 = new Condition(longUri, longString, longString, longString,
+ -1, Condition.STATE_TRUE, Condition.SOURCE_CONTEXT, Condition.FLAG_RELEVANT_ALWAYS);
+
+ assertThat(cond2.id.toString()).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(cond2.summary).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(cond2.line1).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(cond2.line2).hasLength(Condition.MAX_STRING_LENGTH);
+ }
+
+ @Test
+ public void testLongFields_viaParcel() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ // Set fields via reflection to force them to be long, then parcel and unparcel to make sure
+ // it gets truncated upon unparcelling.
+ Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE, Condition.SOURCE_CONTEXT);
+
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+ Field id = Class.forName(CLASS).getDeclaredField("id");
+ id.setAccessible(true);
+ id.set(cond, longUri);
+ Field summary = Class.forName(CLASS).getDeclaredField("summary");
+ summary.setAccessible(true);
+ summary.set(cond, longString);
+ Field line1 = Class.forName(CLASS).getDeclaredField("line1");
+ line1.setAccessible(true);
+ line1.set(cond, longString);
+ Field line2 = Class.forName(CLASS).getDeclaredField("line2");
+ line2.setAccessible(true);
+ line2.set(cond, longString);
+
+ Parcel parcel = Parcel.obtain();
+ cond.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Condition fromParcel = new Condition(parcel);
+ assertThat(fromParcel.id.toString()).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(fromParcel.summary).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(fromParcel.line1).hasLength(Condition.MAX_STRING_LENGTH);
+ assertThat(fromParcel.line2).hasLength(Condition.MAX_STRING_LENGTH);
+ }
+
+ @Test
+ public void testEquals() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ Condition cond1 = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition cond2 = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ "", "", -1,
+ Condition.STATE_TRUE, Condition.SOURCE_SCHEDULE, Condition.FLAG_RELEVANT_ALWAYS);
+
+ assertThat(cond1).isNotEqualTo(cond2);
+ Condition cond3 = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE, Condition.SOURCE_SCHEDULE);
+ assertThat(cond3).isEqualTo(cond2);
+ }
+
+ @Test
+ public void testParcelConstructor() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION);
+
+ Parcel parcel = Parcel.obtain();
+ cond.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Condition fromParcel = new Condition(parcel);
+ assertThat(fromParcel).isEqualTo(cond);
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index 9595332afc6c..e1bcd4a0727b 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.SIZE;
@@ -52,12 +53,15 @@ public class InsetsSourceTest {
private final InsetsSource mSource = new InsetsSource(0 /* id */, navigationBars());
private final InsetsSource mImeSource = new InsetsSource(1 /* id */, ime());
+ private final InsetsSource mImeCaptionSource = new InsetsSource(
+ ID_IME_CAPTION_BAR, captionBar());
private final InsetsSource mCaptionSource = new InsetsSource(2 /* id */, captionBar());
@Before
public void setUp() {
mSource.setVisible(true);
mImeSource.setVisible(true);
+ mImeCaptionSource.setVisible(true);
mCaptionSource.setVisible(true);
}
@@ -110,6 +114,18 @@ public class InsetsSourceTest {
}
@Test
+ public void testCalculateInsets_imeCaptionBar() {
+ mImeCaptionSource.setFrame(new Rect(0, 400, 500, 500));
+ Insets insets = mImeCaptionSource.calculateInsets(new Rect(0, 0, 500, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+
+ // Place caption bar at top; IME caption bar must always return bottom insets
+ mImeCaptionSource.setFrame(new Rect(0, 0, 500, 100));
+ insets = mImeCaptionSource.calculateInsets(new Rect(0, 0, 500, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
public void testCalculateInsets_caption_resizing() {
mCaptionSource.setFrame(new Rect(0, 0, 100, 100));
Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false);
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index dfe6cf81813d..e0e3a3542cb0 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -577,6 +577,58 @@ public class ViewRootImplTest {
});
}
+ /**
+ * Test the accurate aggregation of frame rate values as follows:
+ * 1. When values exceed 60Hz, select the maximum value.
+ * 2. If frame rates are less than 60Hz and multiple frame rates are voted,
+ * prioritize 60Hz..
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_voteFrameRate_aggregate() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.runOnMainSync(() -> {
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ viewRootImpl.votePreferredFrameRate(30);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
+ viewRootImpl.votePreferredFrameRate(120);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ });
+ }
+
+ /**
+ * Override the frame rate category value with setRequestedFrameRate method.
+ * This function can replace the existing frameRateCategory value and
+ * submit your preferred choice to the ViewRootImpl.
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_voteFrameRate_category() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ });
+ }
+
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
index dd116b5b5c35..088b57feb200 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -44,14 +44,19 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.AlertDialog;
import android.app.KeyguardManager;
+import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
+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;
@@ -62,6 +67,7 @@ import android.support.test.uiautomator.Until;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityManager;
@@ -90,12 +96,17 @@ import java.util.Collections;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutChooserActivityTest {
private static final String ONE_HANDED_MODE = "One-Handed mode";
+ private static final String ALLOW_LABEL = "Allow";
private static final String DENY_LABEL = "Deny";
+ private static final String UNINSTALL_LABEL = "Uninstall";
private static final String EDIT_LABEL = "Edit shortcuts";
private static final String LIST_TITLE_LABEL = "Choose features to use";
private static final String TEST_LABEL = "TEST_LABEL";
- private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class");
+ private static final String TEST_PACKAGE = "TEST_LABEL";
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(TEST_PACKAGE,
+ "class");
private static final long UI_TIMEOUT_MS = 1000;
+ private UiAutomation mUiAutomation;
private UiDevice mDevice;
private ActivityScenario<TestAccessibilityShortcutChooserActivity> mScenario;
private TestAccessibilityShortcutChooserActivity mActivity;
@@ -117,6 +128,10 @@ public class AccessibilityShortcutChooserActivityTest {
private IAccessibilityManager mAccessibilityManagerService;
@Mock
private KeyguardManager mKeyguardManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PackageInstaller mPackageInstaller;
@Before
public void setUp() throws Exception {
@@ -125,6 +140,7 @@ public class AccessibilityShortcutChooserActivityTest {
assumeFalse("AccessibilityShortcutChooserActivity not supported on watch",
pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mDevice.wakeUp();
when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
@@ -134,12 +150,15 @@ public class AccessibilityShortcutChooserActivityTest {
when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(
anyInt())).thenReturn(new ParceledListSlice<>(
- Collections.singletonList(mAccessibilityServiceInfo)));
+ Collections.singletonList(mAccessibilityServiceInfo)));
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
anyString(), anyInt(), anyInt())).thenReturn(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+ when(mPackageManager.getPackageInstaller()).thenReturn(mPackageInstaller);
+
TestAccessibilityShortcutChooserActivity.setupForTesting(
- mAccessibilityManagerService, mKeyguardManager);
+ mAccessibilityManagerService, mKeyguardManager,
+ mPackageManager);
}
@After
@@ -150,18 +169,12 @@ public class AccessibilityShortcutChooserActivityTest {
}
@Test
- public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
+ @RequiresFlagsDisabled(Flags.FLAG_DEDUPLICATE_ACCESSIBILITY_WARNING_DIALOG)
+ public void selectTestService_oldPermissionDialog_deny_dialogIsHidden() {
launchActivity();
openShortcutsList();
- // Performing the double-click is flaky so retry if needed.
- for (int attempt = 1; attempt <= 2; attempt++) {
- onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
- if (mDevice.wait(Until.hasObject(By.text(DENY_LABEL)), UI_TIMEOUT_MS)) {
- break;
- }
- }
-
+ mDevice.findObject(By.text(TEST_LABEL)).clickAndWait(Until.newWindow(), UI_TIMEOUT_MS);
onView(withText(DENY_LABEL)).perform(scrollTo(), click());
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -170,6 +183,50 @@ public class AccessibilityShortcutChooserActivityTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DEDUPLICATE_ACCESSIBILITY_WARNING_DIALOG)
+ public void selectTestService_permissionDialog_allow_rowChecked() {
+ launchActivity();
+ openShortcutsList();
+
+ mDevice.findObject(By.text(TEST_LABEL)).clickAndWait(Until.newWindow(), UI_TIMEOUT_MS);
+ clickSystemDialogButton(ALLOW_LABEL);
+
+ assertThat(mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)),
+ UI_TIMEOUT_MS)).isTrue();
+ assertThat(mDevice.wait(Until.hasObject(By.checked(true)), UI_TIMEOUT_MS)).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DEDUPLICATE_ACCESSIBILITY_WARNING_DIALOG)
+ public void selectTestService_permissionDialog_deny_rowNotChecked() {
+ launchActivity();
+ openShortcutsList();
+
+ mDevice.findObject(By.text(TEST_LABEL)).clickAndWait(Until.newWindow(), UI_TIMEOUT_MS);
+ clickSystemDialogButton(DENY_LABEL);
+
+ assertThat(mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)),
+ UI_TIMEOUT_MS)).isTrue();
+ assertThat(mDevice.wait(Until.hasObject(By.checked(true)), UI_TIMEOUT_MS)).isFalse();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DEDUPLICATE_ACCESSIBILITY_WARNING_DIALOG)
+ public void selectTestService_permissionDialog_uninstall_callsUninstaller_rowRemoved() {
+ launchActivity();
+ openShortcutsList();
+
+ mDevice.findObject(By.text(TEST_LABEL)).clickAndWait(Until.newWindow(), UI_TIMEOUT_MS);
+ clickSystemDialogButton(UNINSTALL_LABEL);
+
+ verify(mPackageInstaller).uninstall(eq(TEST_PACKAGE), any());
+ assertThat(mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)),
+ UI_TIMEOUT_MS)).isTrue();
+ assertThat(mDevice.wait(Until.hasObject(By.textStartsWith(TEST_LABEL)),
+ UI_TIMEOUT_MS)).isFalse();
+ }
+
+ @Test
public void clickServiceTarget_notPermittedByAdmin_sendRestrictedDialogIntent()
throws Exception {
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
@@ -239,6 +296,18 @@ public class AccessibilityShortcutChooserActivityTest {
mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)), UI_TIMEOUT_MS);
}
+ private void clickSystemDialogButton(String dialogButtonText) {
+ // Use UiAutomation to find the button because UiDevice struggles to find
+ // a UI element in a system dialog.
+ final AccessibilityNodeInfo button =
+ mUiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByText(dialogButtonText).stream()
+ .filter(AccessibilityNodeInfo::isClickable).findFirst().get();
+ final Rect bounds = new Rect();
+ button.getBoundsInScreen(bounds);
+ mDevice.click(bounds.centerX(), bounds.centerY());
+ }
+
/**
* Used for testing.
*/
@@ -246,12 +315,30 @@ public class AccessibilityShortcutChooserActivityTest {
AccessibilityShortcutChooserActivity {
private static IAccessibilityManager sAccessibilityManagerService;
private static KeyguardManager sKeyguardManager;
+ private static PackageManager sPackageManager;
public static void setupForTesting(
IAccessibilityManager accessibilityManagerService,
- KeyguardManager keyguardManager) {
+ KeyguardManager keyguardManager,
+ PackageManager packageManager) {
sAccessibilityManagerService = accessibilityManagerService;
sKeyguardManager = keyguardManager;
+ sPackageManager = packageManager;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (Flags.deduplicateAccessibilityWarningDialog()) {
+ // Setting the Theme is necessary here for the dialog to use the proper style
+ // resources as designated in its layout XML.
+ setTheme(R.style.Theme_DeviceDefault_DayNight);
+ }
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return sPackageManager;
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java
new file mode 100644
index 000000000000..b76dd51d3f2b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.accessibility.dialog;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.os.RemoteException;
+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.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.R;
+import com.android.internal.accessibility.TestUtils;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Unit Tests for
+ * {@link com.android.internal.accessibility.dialog.AccessibilityServiceWarning}
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@RequiresFlagsEnabled(
+ android.view.accessibility.Flags.FLAG_DEDUPLICATE_ACCESSIBILITY_WARNING_DIALOG)
+public class AccessibilityServiceWarningTest {
+ private static final String A11Y_SERVICE_PACKAGE_LABEL = "TestA11yService";
+ private static final String A11Y_SERVICE_SUMMARY = "TestA11yService summary";
+ private static final String A11Y_SERVICE_COMPONENT_NAME =
+ "fake.package/test.a11yservice.name";
+
+ private Context mContext;
+ private AccessibilityServiceInfo mAccessibilityServiceInfo;
+ private AtomicBoolean mAllowListener;
+ private AtomicBoolean mDenyListener;
+ private AtomicBoolean mUninstallListener;
+
+ @Rule
+ public final Expect expect = Expect.create();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mAccessibilityServiceInfo = TestUtils.createFakeServiceInfo(
+ A11Y_SERVICE_PACKAGE_LABEL,
+ A11Y_SERVICE_SUMMARY,
+ A11Y_SERVICE_COMPONENT_NAME,
+ /* isAlwaysOnService*/ false);
+ mAllowListener = new AtomicBoolean(false);
+ mDenyListener = new AtomicBoolean(false);
+ mUninstallListener = new AtomicBoolean(false);
+ }
+
+ @Test
+ public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams() {
+ final AlertDialog dialog =
+ AccessibilityServiceWarning.createAccessibilityServiceWarningDialog(
+ mContext,
+ mAccessibilityServiceInfo,
+ null, null, null);
+ final Window dialogWindow = dialog.getWindow();
+ assertThat(dialogWindow).isNotNull();
+
+ expect.that(dialogWindow.getAttributes().privateFlags
+ & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ expect.that(dialogWindow.getAttributes().type).isEqualTo(TYPE_SYSTEM_DIALOG);
+ }
+
+ @Test
+ public void createAccessibilityServiceWarningDialog_hasExpectedServiceName() {
+ final TextView title = createDialogContentView().findViewById(
+ R.id.accessibility_permissionDialog_title);
+ assertThat(title).isNotNull();
+
+ assertThat(title.getText().toString()).contains(A11Y_SERVICE_PACKAGE_LABEL);
+ }
+
+ @Test
+ public void createAccessibilityServiceWarningDialog_clickAllow() {
+ final View allowButton = createDialogContentView().findViewById(
+ R.id.accessibility_permission_enable_allow_button);
+ assertThat(allowButton).isNotNull();
+
+ allowButton.performClick();
+
+ expect.that(mAllowListener.get()).isTrue();
+ expect.that(mDenyListener.get()).isFalse();
+ expect.that(mUninstallListener.get()).isFalse();
+ }
+
+ @Test
+ public void createAccessibilityServiceWarningDialog_clickDeny() {
+ final View denyButton = createDialogContentView().findViewById(
+ R.id.accessibility_permission_enable_deny_button);
+ assertThat(denyButton).isNotNull();
+
+ denyButton.performClick();
+
+ expect.that(mAllowListener.get()).isFalse();
+ expect.that(mDenyListener.get()).isTrue();
+ expect.that(mUninstallListener.get()).isFalse();
+ }
+
+ @Test
+ public void createAccessibilityServiceWarningDialog_clickUninstall() {
+ final View uninstallButton = createDialogContentView().findViewById(
+ R.id.accessibility_permission_enable_uninstall_button);
+ assertThat(uninstallButton).isNotNull();
+
+ uninstallButton.performClick();
+
+ expect.that(mAllowListener.get()).isFalse();
+ expect.that(mDenyListener.get()).isFalse();
+ expect.that(mUninstallListener.get()).isTrue();
+ }
+
+ @Test
+ public void getTouchConsumingListener() {
+ final View allowButton = createDialogContentView().findViewById(
+ R.id.accessibility_permission_enable_allow_button);
+ assertThat(allowButton).isNotNull();
+ final View.OnTouchListener listener =
+ AccessibilityServiceWarning.getTouchConsumingListener();
+
+ expect.that(listener.onTouch(allowButton, createMotionEvent(0))).isFalse();
+ expect.that(listener.onTouch(allowButton,
+ createMotionEvent(MotionEvent.FLAG_WINDOW_IS_OBSCURED))).isTrue();
+ expect.that(listener.onTouch(allowButton,
+ createMotionEvent(MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED))).isTrue();
+ }
+
+ private View createDialogContentView() {
+ return AccessibilityServiceWarning.createAccessibilityServiceWarningDialogContentView(
+ mContext,
+ mAccessibilityServiceInfo,
+ (v) -> mAllowListener.set(true),
+ (v) -> mDenyListener.set(true),
+ (v) -> mUninstallListener.set(true));
+ }
+
+ private MotionEvent createMotionEvent(int flags) {
+ MotionEvent.PointerProperties[] props = new MotionEvent.PointerProperties[]{
+ new MotionEvent.PointerProperties()
+ };
+ MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[]{
+ new MotionEvent.PointerCoords()
+ };
+ return MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 1, props, coords,
+ 0, 0, 0, 0, -1, 0, InputDevice.SOURCE_TOUCHSCREEN, flags);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java b/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java
new file mode 100644
index 000000000000..725885a84873
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.jank;
+
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_120_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_240_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_30_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_60_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_90_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.getRefreshRate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class DisplayRefreshRateTest {
+ @Test
+ public void testRefreshRateMapping() {
+ assertThat(getRefreshRate((long) 1e9 / 30)).isEqualTo(REFRESH_RATE_30_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 60)).isEqualTo(REFRESH_RATE_60_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 90)).isEqualTo(REFRESH_RATE_90_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 120)).isEqualTo(REFRESH_RATE_120_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 240)).isEqualTo(REFRESH_RATE_240_HZ);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index dbbd70525d28..1b9717a6dfc5 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -70,6 +70,8 @@ import java.util.concurrent.TimeUnit;
@SmallTest
public class FrameTrackerTest {
private static final String CUJ_POSTFIX = "";
+ private static final long FRAME_TIME_60Hz = (long) 1e9 / 60;
+
private ViewAttachTestActivity mActivity;
@Rule
@@ -170,6 +172,7 @@ public class FrameTrackerTest {
verify(tracker, never()).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -207,6 +210,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -244,6 +248,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -281,6 +286,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -321,6 +327,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -363,6 +370,7 @@ public class FrameTrackerTest {
verify(tracker, never()).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -491,6 +499,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -528,6 +537,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -565,6 +575,7 @@ public class FrameTrackerTest {
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -596,6 +607,7 @@ public class FrameTrackerTest {
verify(tracker).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(6L) /* totalFrames */,
eq(5L) /* missedFrames */,
@@ -648,7 +660,7 @@ public class FrameTrackerTest {
final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(tracker).postCallback(captor.capture());
mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
- new JankData(vsyncId, jankType)
+ new JankData(vsyncId, jankType, FRAME_TIME_60Hz)
});
captor.getValue().run();
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 3e3c77b7b21c..03c38cc137ad 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -25,9 +25,11 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.annotation.EnforcePermission;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.test.FakePermissionEnforcer;
import androidx.test.filters.SmallTest;
@@ -57,7 +59,8 @@ public final class DeviceStateManagerGlobalTest {
@Before
public void setUp() {
- mService = new TestDeviceStateManagerService();
+ FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+ mService = new TestDeviceStateManagerService(permissionEnforcer);
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
assertFalse(mService.mCallbacks.isEmpty());
}
@@ -261,6 +264,10 @@ public final class DeviceStateManagerGlobalTest {
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
+ TestDeviceStateManagerService(FakePermissionEnforcer enforcer) {
+ super(enforcer);
+ }
+
private DeviceStateInfo getInfo() {
final int mergedBaseState = mBaseStateRequest == null
? mBaseState : mBaseStateRequest.state;
@@ -380,7 +387,10 @@ public final class DeviceStateManagerGlobalTest {
// No-op in the test since DeviceStateManagerGlobal just calls into the system server with
// no business logic around it.
@Override
- public void onStateRequestOverlayDismissed(boolean shouldCancelMode) {}
+ @EnforcePermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void onStateRequestOverlayDismissed(boolean shouldCancelMode) {
+ onStateRequestOverlayDismissed_enforcePermission();
+ }
public void setSupportedStates(int[] states) {
mSupportedStates = states;
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 580e73c331a1..06340a222ac6 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -35,6 +35,7 @@ android_test {
"androidx.test.ext.junit",
"truth",
"servicestests-utils",
+ "ravenwood-junit",
],
libs: [
@@ -50,3 +51,22 @@ android_test {
test_suites: ["device-tests"],
}
+
+android_ravenwood_test {
+ name: "FrameworksUtilTestsRavenwood",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ ],
+ srcs: [
+ "src/android/util/DataUnitTest.java",
+ "src/android/util/EventLogTest.java",
+ "src/android/util/IndentingPrintWriterTest.java",
+ "src/android/util/IntArrayTest.java",
+ "src/android/util/LocalLogTest.java",
+ "src/android/util/LongArrayTest.java",
+ "src/android/util/SlogTest.java",
+ "src/android/util/TimeUtilsTest.java",
+ ],
+ auto_gen_config: true,
+}
diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/utiltests/src/android/util/DataUnitTest.java
index 034cbddc0144..af9ebc833d4b 100644
--- a/core/tests/coretests/src/android/util/DataUnitTest.java
+++ b/core/tests/utiltests/src/android/util/DataUnitTest.java
@@ -16,12 +16,18 @@
package android.util;
+import static org.junit.Assert.assertEquals;
+
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
-public class DataUnitTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class DataUnitTest {
+ @Test
public void testSi() throws Exception {
assertEquals(12_000L, DataUnit.KILOBYTES.toBytes(12));
assertEquals(12_000_000L, DataUnit.MEGABYTES.toBytes(12));
@@ -29,6 +35,7 @@ public class DataUnitTest extends TestCase {
assertEquals(12_000_000_000_000L, DataUnit.TERABYTES.toBytes(12));
}
+ @Test
public void testIec() throws Exception {
assertEquals(12_288L, DataUnit.KIBIBYTES.toBytes(12));
assertEquals(12_582_912L, DataUnit.MEBIBYTES.toBytes(12));
diff --git a/core/tests/coretests/src/android/util/EventLogTest.java b/core/tests/utiltests/src/android/util/EventLogTest.java
index 94e72c4a8d52..0ebf2c163d19 100644
--- a/core/tests/coretests/src/android/util/EventLogTest.java
+++ b/core/tests/utiltests/src/android/util/EventLogTest.java
@@ -16,23 +16,42 @@
package android.util;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.EventLog.Event;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import junit.framework.AssertionFailedError;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
/** Unit tests for {@link android.util.EventLog} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class EventLogTest {
+ @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
+ public void testSimple() throws Throwable {
+ EventLog.writeEvent(42, 42);
+ EventLog.writeEvent(42, 42L);
+ EventLog.writeEvent(42, 42f);
+ EventLog.writeEvent(42, "forty-two");
+ EventLog.writeEvent(42, 42, "forty-two", null, 42);
+ }
@Test
+ @IgnoreUnderRavenwood(reason = "Reading not yet supported")
public void testWithNewData() throws Throwable {
Event event = createEvent(() -> {
EventLog.writeEvent(314, 123);
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/utiltests/src/android/util/LocalLogTest.java
index d4861cd391a8..5025a3d0980c 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/utiltests/src/android/util/LocalLogTest.java
@@ -16,9 +16,13 @@
package android.util;
+import static org.junit.Assert.assertTrue;
+
import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -27,13 +31,16 @@ import java.util.Collections;
import java.util.List;
@LargeTest
-public class LocalLogTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class LocalLogTest {
+ @Test
public void testA_localTimestamps() {
boolean localTimestamps = true;
doTestA(localTimestamps);
}
+ @Test
public void testA_nonLocalTimestamps() {
boolean localTimestamps = false;
doTestA(localTimestamps);
@@ -49,6 +56,7 @@ public class LocalLogTest extends TestCase {
testcase(new LocalLog(10, localTimestamps), lines, want);
}
+ @Test
public void testB() {
String[] lines = {
"foo",
@@ -59,6 +67,7 @@ public class LocalLogTest extends TestCase {
testcase(new LocalLog(0), lines, want);
}
+ @Test
public void testC() {
String[] lines = {
"dropped",
diff --git a/core/tests/utiltests/src/android/util/SlogTest.java b/core/tests/utiltests/src/android/util/SlogTest.java
new file mode 100644
index 000000000000..6f761e348dbe
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/SlogTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SlogTest {
+ private static final String TAG = "tag";
+ private static final String MSG = "msg";
+ private static final Throwable THROWABLE = new Throwable();
+
+ @Test
+ public void testSimple() {
+ Slog.v(TAG, MSG);
+ Slog.d(TAG, MSG);
+ Slog.i(TAG, MSG);
+ Slog.w(TAG, MSG);
+ Slog.e(TAG, MSG);
+ }
+
+ @Test
+ public void testThrowable() {
+ Slog.v(TAG, MSG, THROWABLE);
+ Slog.d(TAG, MSG, THROWABLE);
+ Slog.i(TAG, MSG, THROWABLE);
+ Slog.w(TAG, MSG, THROWABLE);
+ Slog.e(TAG, MSG, THROWABLE);
+ }
+}
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
new file mode 100644
index 000000000000..e8246c83e086
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeUtilsTest {
+ public static final long SECOND_IN_MILLIS = 1000;
+ public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+ public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+ public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+ public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+
+ @Test
+ public void testFormatTime() {
+ assertEquals("1672556400000 (now)",
+ TimeUtils.formatTime(1672556400000L, 1672556400000L));
+ assertEquals("1672556400000 (in 10 ms)",
+ TimeUtils.formatTime(1672556400000L, 1672556400000L - 10));
+ assertEquals("1672556400000 (10 ms ago)",
+ TimeUtils.formatTime(1672556400000L, 1672556400000L + 10));
+
+ // Uses formatter above, so we just care that it doesn't crash
+ TimeUtils.formatRealtime(1672556400000L);
+ TimeUtils.formatUptime(1672556400000L);
+ }
+
+ @Test
+ public void testFormatDuration_Zero() {
+ assertEquals("0", TimeUtils.formatDuration(0));
+ }
+
+ @Test
+ public void testFormatDuration_Negative() {
+ assertEquals("-10ms", TimeUtils.formatDuration(-10));
+ }
+
+ @Test
+ public void testFormatDuration() {
+ long accum = 900;
+ assertEquals("+900ms", TimeUtils.formatDuration(accum));
+
+ accum += 59 * SECOND_IN_MILLIS;
+ assertEquals("+59s900ms", TimeUtils.formatDuration(accum));
+
+ accum += 59 * MINUTE_IN_MILLIS;
+ assertEquals("+59m59s900ms", TimeUtils.formatDuration(accum));
+
+ accum += 23 * HOUR_IN_MILLIS;
+ assertEquals("+23h59m59s900ms", TimeUtils.formatDuration(accum));
+
+ accum += 6 * DAY_IN_MILLIS;
+ assertEquals("+6d23h59m59s900ms", TimeUtils.formatDuration(accum));
+ }
+
+ @Test
+ public void testDumpTime() {
+ assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
+ TimeUtils.dumpTime(pw, 1672556400000L);
+ }));
+ assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> {
+ TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L);
+ }));
+ assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
+ TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10);
+ }));
+ }
+
+ @Test
+ public void testFormatForLogging() {
+ assertEquals("unknown", TimeUtils.formatForLogging(0));
+ assertEquals("unknown", TimeUtils.formatForLogging(-1));
+ assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE));
+ assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L));
+ }
+
+ @Test
+ public void testLogTimeOfDay() {
+ assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
+ }
+
+ public static String runWithPrintWriter(Consumer<PrintWriter> consumer) {
+ final StringWriter sw = new StringWriter();
+ consumer.accept(new PrintWriter(sw));
+ return sw.toString();
+ }
+
+ public static String runWithStringBuilder(Consumer<StringBuilder> consumer) {
+ final StringBuilder sb = new StringBuilder();
+ consumer.accept(sb);
+ return sb.toString();
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3218666771df..1f08955e5f58 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -467,6 +467,7 @@ applications that come with the platform
<permission name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
<permission name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE" />
<permission name="android.permission.MANAGE_APP_HIBERNATION"/>
+ <permission name="android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO" />
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
<!-- Permission required for CTS test - MediaCodecResourceTest -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index dc2b0561957d..f19acbe023b8 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -415,12 +415,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-1717147904": {
- "message": "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"-1710206702": {
"message": "Display id=%d is frozen while keyguard locked, return %d",
"level": "VERBOSE",
@@ -817,6 +811,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1325565952": {
+ "message": "Attempted to get home support flag of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-1323783276": {
"message": "performEnableScreen: bootFinished() failed.",
"level": "WARN",
@@ -1213,12 +1213,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-997565097": {
- "message": "Focused window found using getFocusedWindowToken",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"-993378225": {
"message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s",
"level": "VERBOSE",
@@ -2233,6 +2227,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-98422345": {
+ "message": "Focus window is closing.",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/BackNavigationController.java"
+ },
"-91393839": {
"message": "Set animatingExit: reason=remove\/applyAnimation win=%s",
"level": "VERBOSE",
@@ -2731,12 +2731,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "309039362": {
- "message": "SURFACE MATRIX [%f,%f,%f,%f]: %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"312030608": {
"message": "New topFocusedDisplayId=%d",
"level": "DEBUG",
@@ -3091,12 +3085,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "633654009": {
- "message": "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"638429464": {
"message": "\tRemove container=%s",
"level": "DEBUG",
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 51b720ddb758..f9347ee6ea09 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -324,7 +324,7 @@ key 362 GUIDE
# key 365 "KEY_EPG"
key 366 DVR
# key 367 "KEY_MHP"
-# key 368 "KEY_LANGUAGE"
+key 368 LANGUAGE_SWITCH
# key 369 "KEY_TITLE"
key 370 CAPTIONS
# key 371 "KEY_ANGLE"
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 07f1d4a09006..8dc9579e6b52 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -63,7 +63,7 @@ public class HideInCommentsChecker extends BugChecker implements
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
- final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree);
+ final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
final String sourceCode = state.getSourceCode().toString();
for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
for (Tokens.Comment comment : token.comments()) {
@@ -112,9 +112,9 @@ public class HideInCommentsChecker extends BugChecker implements
}
- private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) {
+ private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree, VisitorState state) {
Map<Integer, Tree> javadoccableTrees = new HashMap<>();
- new SuppressibleTreePathScanner<Void, Void>() {
+ new SuppressibleTreePathScanner<Void, Void>(state) {
@Override
public Void visitClass(ClassTree classTree, Void unused) {
javadoccableTrees.put(getStartPosition(classTree), classTree);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 2ec4524e1241..d659ddd75f72 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -402,8 +402,8 @@ public class BaseRecordingCanvas extends Canvas {
}
@Override
- public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
- @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ public final void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
+ @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
nDrawDoubleRoundRect(mNativeCanvasWrapper,
outer.left, outer.top, outer.right, outer.bottom, outerRadii,
inner.left, inner.top, inner.right, inner.bottom, innerRadii,
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index 66fabec91924..a4bce9eb5e88 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -23,6 +23,8 @@ import android.annotation.NonNull;
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.Buffer;
import java.nio.ShortBuffer;
@@ -43,6 +45,7 @@ public class Mesh {
* Determines how the mesh is represented and will be drawn.
*/
@IntDef({TRIANGLES, TRIANGLE_STRIP})
+ @Retention(RetentionPolicy.SOURCE)
private @interface Mode {}
/**
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 5509f000aca5..45e29a88c7db 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -62,6 +62,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Objects;
@@ -116,6 +118,7 @@ public final class Icon implements Parcelable {
*/
@IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP,
TYPE_URI_ADAPTIVE_BITMAP})
+ @Retention(RetentionPolicy.SOURCE)
public @interface IconType {
}
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index b7ea04fdfe07..2beb434566e5 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -51,7 +51,7 @@ public class AndroidKeyStoreMaintenance {
* @return 0 if successful or a {@code ResponseCode}
* @hide
*/
- public static int onUserAdded(@NonNull int userId) {
+ public static int onUserAdded(int userId) {
StrictMode.noteDiskWrite();
try {
getService().onUserAdded(userId);
@@ -66,6 +66,30 @@ public class AndroidKeyStoreMaintenance {
}
/**
+ * Tells Keystore to create a user's super keys and store them encrypted by the given secret.
+ *
+ * @param userId - Android user id of the user
+ * @param password - a secret derived from the user's synthetic password
+ * @param allowExisting - true if the keys already existing should not be considered an error
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int initUserSuperKeys(int userId, @NonNull byte[] password,
+ boolean allowExisting) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().initUserSuperKeys(userId, password, allowExisting);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "initUserSuperKeys failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 about removing a user
*
* @param userId - Android user id of the user being removed
@@ -110,6 +134,28 @@ public class AndroidKeyStoreMaintenance {
}
/**
+ * Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
+ * Swipe or None. Keystore uses this notification to delete the user's auth-bound keys.
+ *
+ * @param userId - Android user id of the user
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserLskfRemoved(int userId) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().onUserLskfRemoved(userId);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserLskfRemoved failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to
* be cleared.
*/
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index b7140355e489..231fa4837441 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1282,15 +1282,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* function (MGF1) with a digest.
* The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation
* time if no digests have been explicitly provided.
- * When using the key, the caller may not specify any digests that were not provided during
- * key creation time. The caller may specify the default digest, {@code SHA-1}, if no
+ * {@code null} may not be specified as a parameter to this method: It is not possible to
+ * disable MGF1 digest, a default must be present for when the caller tries to use it.
+ *
+ * <p>When using the key, the caller may not specify any digests that were not provided
+ * during key creation time. The caller may specify the default digest, {@code SHA-1}, if no
* digests were explicitly provided during key creation (but it is not necessary to do so).
*
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
@FlaggedApi("MGF1_DIGEST_SETTER")
- public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 4d73c20fe39f..ca3d8d18db83 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -375,7 +375,8 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
return TaskFragmentAnimationParams.DEFAULT;
}
return new TaskFragmentAnimationParams.Builder()
- .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
+ // TODO(b/263047900): Update extensions API.
+ // .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
.build();
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index faf7c3999402..b5c32bbe78fa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -854,7 +854,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return new SplitAttributes.Builder()
.setSplitType(splitTypeToUpdate)
.setLayoutDirection(splitAttributes.getLayoutDirection())
- .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
+ // TODO(b/263047900): Update extensions API.
+ // .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
.build();
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index 9607b78bacf0..60beb0b7f0a4 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -17,6 +17,7 @@
package androidx.window.extensions;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityTaskManager;
@@ -69,6 +70,7 @@ public class WindowExtensionsTest {
.isEqualTo(SplitAttributes.LayoutDirection.LOCALE);
assertThat(splitAttributes.getSplitType())
.isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f));
- assertThat(splitAttributes.getAnimationBackgroundColor()).isEqualTo(0);
+ // TODO(b/263047900): Update extensions API.
+ // assertThat(splitAttributes.getAnimationBackgroundColor()).isEqualTo(0);
}
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 29bdd5ce0c9e..4d2d960822d1 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -35,3 +35,18 @@ flag {
description: "Enables taskbar / navbar unification"
bug: "309671494"
}
+
+flag {
+ name: "enable_pip_ui_state_on_entering"
+ namespace: "multitasking"
+ description: "Enables PiP UI state callback on entering"
+ bug: "303718131"
+}
+
+flag {
+ name: "enable_pip2_implementation"
+ namespace: "multitasking"
+ description: "Enables the new implementation of PiP (PiP2)"
+ bug: "290220798"
+ is_fixed_read_only: true
+}
diff --git a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
index 5c8c84cbb85b..ed00a87b0fa5 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
@@ -21,8 +21,8 @@
android:layout_width="wrap_content"
android:paddingTop="48dp"
android:paddingBottom="48dp"
- android:paddingEnd="@dimen/bubble_user_education_padding_end"
- android:layout_marginEnd="@dimen/bubble_user_education_margin_end"
+ android:paddingHorizontal="@dimen/bubble_user_education_padding_horizontal"
+ android:layout_marginEnd="@dimen/bubble_user_education_margin_horizontal"
android:orientation="vertical"
android:background="@drawable/bubble_stack_user_education_bg"
>
diff --git a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
index b28f58f8356d..4f6bdfd98d54 100644
--- a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
@@ -23,8 +23,8 @@
android:clickable="true"
android:paddingTop="28dp"
android:paddingBottom="16dp"
- android:paddingEnd="@dimen/bubble_user_education_padding_end"
- android:layout_marginEnd="@dimen/bubble_user_education_margin_end"
+ android:paddingEnd="@dimen/bubble_user_education_padding_horizontal"
+ android:layout_marginEnd="@dimen/bubble_user_education_margin_horizontal"
android:orientation="vertical"
android:background="@drawable/bubble_stack_user_education_bg"
>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 202ea957971e..90b13cdc79b4 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"App sal dalk nie met verdeelde skerm werk nie"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App steun nie verdeelde skerm nie"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Hierdie app kan net in 1 venster oopgemaak word."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Hierdie app kan net in 1 venster oopgemaak word"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Program steun nie begin op sekondêre skerms nie."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skermverdeler"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 4071e790d55a..478585aace68 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"መተግበሪያ ከተከፈለ ማያ ገፅ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ይህ መተግበሪያ መከፈት የሚችለው በ1 መስኮት ብቻ ነው።"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ይህ መተግበሪያ መከፈት የሚችለው በ1 መስኮት ብቻ ነው"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"መተግበሪያ በሁለተኛ ማሳያዎች ላይ ማስጀመርን አይደግፍም።"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"የተከፈለ የማያ ገፅ ከፋይ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index d3890a779f87..b2a522c89f8c 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"قد لا يعمل التطبيق بشكل سليم في وضع تقسيم الشاشة."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"لا يعمل التطبيق في وضع تقسيم الشاشة."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"لا يمكن فتح هذا التطبيق إلا في نافذة واحدة."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"لا يمكن فتح هذا التطبيق إلا في نافذة واحدة."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"أداة تقسيم الشاشة"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 05b8f7dca729..897c38f66e4b 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই এপ্‌টো কেৱল ১ খন ৱিণ্ড’ত খুলিব পাৰি।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"এই এপ্‌টো কেৱল ১ খন ৱিণ্ড’ত খুলিব পাৰি"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 108593e4b948..4854e0db7ed5 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Tətbiq bölünmüş ekranda işləməyə bilər"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Tətbiq bölünmüş ekranı dəstəkləmir"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu tətbiq yalnız 1 pəncərədə açıla bilər."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Bu tətbiq yalnız 1 pəncərədə açıla bilər"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Bölünmüş ekran ayırıcısı"</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 76fd5b1c2d67..cac4e67b1cfc 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacija ne podržava podeljeni ekran."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija može da se otvori samo u jednom prozoru."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova aplikacija može da se otvori samo u jednom prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdelnik podeljenog ekrana"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 473d15acc632..cac76df15910 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Праграма можа не працаваць у рэжыме падзеленага экрана"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Праграма не падтрымлівае рэжым падзеленага экрана"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Гэту праграму можна адкрыць толькі ў адным акне."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Гэту праграму можна адкрыць толькі ў адным акне"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Праграма не падтрымлівае запуск на дадатковых экранах."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Раздзяляльнік падзеленага экрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 7aa98e5bc218..ac9a20806b72 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Приложението може да не работи в режим на разделен екран"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Приложението не поддържа разделен екран"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Това приложение може да се отвори само в 1 прозорец."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Това приложение може да се отвори само в 1 прозорец"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложението не поддържа използването на алтернативни дисплеи."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Разделител в режима за разделен екран"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index caad87e8f05f..3b83dcb461ee 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"স্প্লিট স্ক্রিনে এই অ্যাপ নাও কাজ করতে পারে"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"স্প্লিট স্ক্রিনে এই অ্যাপ কাজ করে না"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই অ্যাপটি শুধু ১টি উইন্ডোয় খোলা যেতে পারে।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"এই অ্যাপটি শুধুমাত্র ১টি উইন্ডোতে খোলা যেতে পারে"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"সেকেন্ডারি ডিসপ্লেতে অ্যাপ লঞ্চ করা যাবে না।"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"স্প্লিট স্ক্রিন বিভাজক"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 66e67b385cb8..813d163d07fe 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacija možda neće funkcionirati na podijeljenom ekranu"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacija ne podržava podijeljeni ekran"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija se može otvoriti samo u 1 prozoru."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova aplikacija se može otvoriti samo u 1 prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdjelnik podijeljenog ekrana"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 62399ae07e7d..d00c50bb0294 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"És possible que l\'aplicació no funcioni amb la pantalla dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"L\'aplicació no admet la pantalla dividida"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aquesta aplicació només pot obrir-se en 1 finestra."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aquesta aplicació només pot obrir-se en 1 finestra"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Separador de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 8e0aba0fdec6..40132e4f67f8 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikace v režimu rozdělené obrazovky nemusí fungovat"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikace nepodporuje režim rozdělené obrazovky"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tuto aplikaci lze otevřít jen na jednom okně."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Tuto aplikaci lze otevřít jen v jednom okně"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Čára rozdělující obrazovku"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index d3989bcbd6f0..6e9738dc7398 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Appen fungerer muligvis ikke i opdelt skærm"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Appen understøtter ikke opdelt skærm"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne app kan kun åbnes i 1 vindue."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Denne app kan kun åbnes i 1 vindue"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Adskiller til opdelt skærm"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 5d0ee2951e90..5da224d35360 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Die App funktioniert im Splitscreen-Modus unter Umständen nicht"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Splitscreen wird in dieser App nicht unterstützt"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Diese App kann nur in einem einzigen Fenster geöffnet werden."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Diese App kann nur in einem einzigen Fenster geöffnet werden"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Bildschirmteiler"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 2b73528f7aef..822b5526c6fd 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Αυτή η εφαρμογή μπορεί να ανοιχθεί μόνο σε 1 παράθυρο."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Αυτή η εφαρμογή μπορεί να ανοίξει μόνο σε ένα παράθυρο"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Η εφαρμογή δεν υποστηρίζει την εκκίνηση σε δευτερεύουσες οθόνες."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Διαχωριστικό οθόνης"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 2f0e898d5db7..76464b398f89 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"App may not work with split screen"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App does not support split screen"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index a338905fa299..c0c46cd608ee 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"App may not work with split screen"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App does not support split screen"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in 1 window."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in 1 window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 2f0e898d5db7..76464b398f89 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"App may not work with split screen"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App does not support split screen"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 2f0e898d5db7..76464b398f89 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"App may not work with split screen"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App does not support split screen"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 20344389ddcc..f089938fd9cb 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎App may not work with split screen‎‏‎‎‏‎"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎App does not support split screen‎‏‎‎‏‎"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎This app can only be opened in 1 window.‎‏‎‎‏‎"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‎‎This app can only be opened in 1 window‎‏‎‎‏‎"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎App does not support launch on secondary displays.‎‏‎‎‏‎"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎Split screen divider‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index ebf67f6e164e..6bbc1e37671f 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Es posible que la app no funcione en el modo de pantalla dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"La app no es compatible con la función de pantalla dividida"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app solo puede estar abierta en 1 ventana."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta app solo puede estar abierta en 1 ventana"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 05171b2fc6dd..c662ff603dd7 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Puede que la aplicación no funcione con la pantalla dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"La aplicación no es compatible con la pantalla dividida"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación solo puede abrirse en una ventana."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta aplicación solo puede abrirse en una ventana"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 3af3c16ede24..ade5e2d18645 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Rakendus ei pruugi jagatud ekraanikuvaga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Rakendus ei toeta jagatud ekraanikuva."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Selle rakenduse saab avada ainult ühes aknas."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Selle rakenduse saab avada ainult ühes aknas"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Jagatud ekraanikuva jaotur"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 1dbc57b73f6c..d6cb66885cf5 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikazioak ez du onartzen pantaila zatitua"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Leiho bakar batean ireki daiteke aplikazioa."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Leiho bakar batean ireki daiteke aplikazioa"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da exekutatu bigarren mailako pantailatan."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Pantaila-zatitzailea"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index f7547602dcea..ba0f51cb1490 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ممکن است برنامه با صفحهٔ دونیمه کار نکند"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"برنامه از صفحهٔ دونیمه پشتیبانی نمی‌کند"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"این برنامه فقط در ۱ پنجره می‌تواند باز شود."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"این برنامه فقط در ۱ پنجره می‌تواند باز شود"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"برنامه از راه‌اندازی در نمایشگرهای ثانویه پشتیبانی نمی‌کند."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"تقسیم‌کننده صفحهٔ دونیمه"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 9094c7335571..a53f861e1d18 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Sovellus ei ehkä toimi jaetulla näytöllä"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Sovellus ei tue jaetun näytön tilaa"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tämän sovelluksen voi avata vain yhdessä ikkunassa."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Tämän sovelluksen voi avata vain yhdessä ikkunassa"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Näytönjakaja"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index b26c1b4e3018..4563556657af 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"L\'application peut ne pas fonctionner avec l\'écran partagé"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"L\'application ne prend pas en charge l\'écran partagé"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette application ne peut être ouverte que dans une fenêtre."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette application ne peut être ouverte que dans une seule fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Séparateur d\'écran partagé"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 6e6420a22f50..895757184b23 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"L\'appli peut ne pas fonctionner en mode Écran partagé"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Appli incompatible avec l\'écran partagé"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette appli ne peut être ouverte que dans 1 fenêtre."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette appli ne peut être ouverte que dans 1 fenêtre"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Séparateur d\'écran partagé"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index bf3a45b46484..54c864eced47 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"É posible que a aplicación non funcione coa pantalla dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"A aplicación non admite a función de pantalla dividida"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación só se pode abrir en 1 ventá."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta aplicación só se pode abrir en 1 ventá"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 84c818230621..2b092795b035 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"વિભાજિત સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ઍપ વિભાજિત સ્ક્રીનને સપોર્ટ કરતી નથી"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"આ ઍપ માત્ર 1 વિન્ડોમાં ખોલી શકાય છે."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"આ ઍપ માત્ર 1 વિન્ડોમાં ખોલી શકાય છે"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર લૉન્ચનું સમર્થન કરતી નથી."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"સ્ક્રીનને વિભાજિત કરતું વિભાજક"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 8068f50d9cf6..35b099ac6d38 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"मुमकिन है कि ऐप्लिकेशन, स्प्लिट स्क्रीन मोड में काम न करे"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"यह ऐप्लिकेशन, स्प्लिट स्क्रीन मोड पर काम नहीं करता"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"इस ऐप्लिकेशन को सिर्फ़ एक विंडो में खोला जा सकता है."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"इस ऐप्लिकेशन को सिर्फ़ एक विंडो में खोला जा सकता है"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर ऐप लॉन्च नहीं किया जा सकता."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"स्प्लिट स्क्रीन डिवाइडर मोड"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 5f0b866ee4de..f2c3c22414df 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacija ne podržava podijeljeni zaslon"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova se aplikacija može otvoriti samo u jednom prozoru."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova se aplikacija može otvoriti samo u jednom prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdjelnik podijeljenog zaslona"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index d89e2923b5fe..d94bb29f1d73 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Az alkalmazás nem támogatja az osztott képernyőt"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ez az alkalmazás csak egy ablakban nyitható meg."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ez az alkalmazás csak egy ablakban nyitható meg"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Elválasztó az osztott képernyős nézetben"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 9c517b29d291..f2945c16e50b 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Հավելվածը չի աջակցում էկրանի տրոհումը"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Այս հավելվածը հնարավոր է բացել միայն մեկ պատուհանում։"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Այս հավելվածը հնարավոր է բացել միայն մեկ պատուհանում"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Հավելվածը չի աջակցում գործարկումը լրացուցիչ էկրանների վրա"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Տրոհված էկրանի բաժանիչ"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 3dbd75087382..c39b429e489c 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikasi mungkin tidak berfungsi dengan layar terpisah"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikasi tidak mendukung layar terpisah"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplikasi ini hanya dapat dibuka di 1 jendela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aplikasi ini hanya dapat dibuka di 1 jendela"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Pembagi layar terpisah"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 2927d6a0d8ec..630eaa3855c1 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Forritið virkar hugsanlega ekki með skjáskiptingu"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Forritið styður ekki skjáskiptingu"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aðeins er hægt að opna þetta forrit í 1 glugga."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aðeins er hægt að opna þetta forrit í 1 glugga"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skilrúm skjáskiptingar"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 938a81466381..77893c9e38cf 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"L\'app potrebbe non funzionare con lo schermo diviso"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"L\'app non supporta la modalità schermo diviso"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Questa app può essere aperta soltanto in 1 finestra."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Questa app può essere aperta soltanto in 1 finestra"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Strumento per schermo diviso"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index aaa0a3866031..4f28c23ba5df 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"יכול להיות שהאפליקציה לא תפעל עם מסך מפוצל"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"האפליקציה לא תומכת במסך מפוצל"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ניתן לפתוח את האפליקציה הזו רק בחלון אחד."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ניתן לפתוח את האפליקציה הזו רק בחלון אחד"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"האפליקציה אינה תומכת בהפעלה במסכים משניים."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"מחלק מסך מפוצל"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 1979fd52e5bb..60b4d7eb8b4d 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"アプリは分割画面では動作しないことがあります"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"アプリで分割画面がサポートされていません"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"このアプリはウィンドウが 1 つの場合のみ開くことができます。"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"このアプリはウィンドウが 1 つの場合のみ開くことができます"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"アプリはセカンダリ ディスプレイでの起動に対応していません。"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"分割画面の分割線"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index ce1ce6402637..28d2257786a7 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ამ აპის გახსნა შესაძლებელია მხოლოდ 1 ფანჯარაში."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ამ აპის გახსნა შესაძლებელია მხოლოდ 1 ფანჯარაში"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"აპს არ გააჩნია მეორეული ეკრანის მხარდაჭერა."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"ეკრანის გაყოფის გამყოფი"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index b4b05072f4a4..441df8d70e95 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Қолданбада экранды бөлу мүмкін емес."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Бұл қолданбаны тек 1 терезеден ашуға болады."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Бұл қолданбаны тек 1 терезеден ашуға болады."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Қолданба қосымша дисплейлерде іске қосуды қолдамайды."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Экранды бөлу режимінің бөлгіші"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 9b0a0dad5782..efa6418eaa47 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"កម្មវិធី​អាចមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"កម្មវិធីមិនអាចប្រើមុខងារ​បំបែកអេក្រង់បានទេ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"កម្មវិធីនេះអាចបើកនៅក្នុងវិនដូតែ 1 ប៉ុណ្ណោះ។"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"អាចបើកកម្មវិធីនេះបានតែក្នុងវិនដូ 1 ប៉ុណ្ណោះ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"កម្មវិធី​នេះមិន​អាច​ចាប់ផ្តើម​នៅលើ​អេក្រង់បន្ទាប់បន្សំបានទេ។"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"បន្ទាត់ខណ្ឌចែកក្នុងមុខងារ​បំបែកអេក្រង់"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index b19978aa09d7..0cda44509b54 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ಈ ಆ್ಯಪ್ ಅನ್ನು 1 ವಿಂಡೋದಲ್ಲಿ ಮಾತ್ರ ತೆರೆಯಬಹುದು."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ಈ ಆ್ಯಪ್ ಅನ್ನು 1 ವಿಂಡೋದಲ್ಲಿ ಮಾತ್ರ ತೆರೆಯಬಹುದು"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index af08da9d6e65..676506fc68dd 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"앱이 화면 분할 모드로는 작동하지 않을 수 있습니다"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"앱이 화면 분할을 지원하지 않습니다"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"이 앱은 창 1개에서만 열 수 있습니다."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"이 앱은 창 1개에서만 열 수 있습니다."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"앱이 보조 디스플레이에서의 실행을 지원하지 않습니다."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"화면 분할기"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index be24cefbaf91..57253ef55085 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Колдонмодо экран бөлүнбөшү мүмкүн"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Колдонмодо экран бөлүнбөйт"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Бул колдонмону 1 терезеде гана ачууга болот."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Бул колдонмону 1 терезеде гана ачууга болот"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Колдонмону кошумча экрандарда иштетүүгө болбойт."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Экранды бөлгүч"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 41219eec986c..c5f6e2245b31 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບໂໝດແບ່ງໜ້າຈໍ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ແອັບບໍ່ຮອງຮັບການແບ່ງໜ້າຈໍ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ແອັບນີ້ສາມາດເປີດໄດ້ໃນ 1 ໜ້າຈໍເທົ່ານັ້ນ."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ແອັບນີ້ສາມາດເປີດໄດ້ໃນ 1 ໜ້າຈໍເທົ່ານັ້ນ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ແອັບບໍ່ຮອງຮັບການເປີດໃນໜ້າຈໍທີສອງ."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"ເສັ້ນແບ່ງໜ້າຈໍ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b98fce8f8a3f..eeed5a416fdc 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Programa gali neveikti naudojant išskaidyto ekrano režimą"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Programoje nepalaikomas išskaidyto ekrano režimas"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šią programą galima atidaryti tik viename lange."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Šią programą galima atidaryti tik viename lange"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Išskaidyto ekrano režimo daliklis"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 11b645ef80b7..4324d468042b 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Lietotnē netiek atbalstīta ekrāna sadalīšana"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šo lietotni var atvērt tikai vienā logā."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Šo lietotni var atvērt tikai vienā logā"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Ekrāna sadalītājs"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index ba2c97a4f29f..471f5bdfcf1a 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Апликацијата можеби нема да работи со поделен екран"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Апликацијата не поддржува поделен екран"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Апликацијава може да се отвори само во еден прозорец."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Апликацијава може да се отвори само во еден прозорец"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликацијата не поддржува стартување на други екрани."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Разделник на поделен екран"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 8a95d7ef157c..5bc694a10747 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"സ്‌ക്രീൻ വിഭജന മോഡിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ഈ ആപ്പ് ഒരു വിൻഡോയിൽ മാത്രമേ തുറക്കാനാകൂ."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ഈ ആപ്പ് ഒരു വിൻഡോയിൽ മാത്രമേ തുറക്കാനാകൂ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"രണ്ടാം ഡിസ്‌പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"സ്‌ക്രീൻ വിഭജന മോഡ് ഡിവൈഡർ"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index bead7fe0d1c7..0268c649380d 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Апп дэлгэцийг хуваах горимтой ажиллахгүй байж магадгүй"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Апп дэлгэцийг хуваах горимыг дэмждэггүй"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Энэ аппыг зөвхөн 1 цонхонд нээх боломжтой."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Энэ аппыг зөвхөн 1 цонхонд нээх боломжтой"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Аппыг хоёрдогч дэлгэцэд эхлүүлэх боломжгүй."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Дэлгэцийг хуваах хуваагч"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index ca0e15a0241b..2e6163e65668 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करणार नाही"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"अ‍ॅप हे स्प्लिट स्क्रीनला सपोर्ट करत नाही"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"हे अ‍ॅप फक्त एका विंडोमध्ये उघडले जाऊ शकते."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"हे अ‍ॅप फक्त एका विंडोमध्ये उघडले जाऊ शकते"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"दुसऱ्या डिस्प्लेवर अ‍ॅप लाँच होणार नाही."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"स्प्लिट स्क्रीन विभाजक"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index fd9fb87b918c..a60e61b892cb 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Apl mungkin tidak berfungsi dengan skrin pisah"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Apl tidak menyokong skrin pisah"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Apl ini hanya boleh dibuka dalam 1 tetingkap."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Apl ini hanya boleh dibuka dalam 1 tetingkap"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Pembahagi skrin pisah"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 42bce8104a5c..6b91d4676621 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"အက်ပ်တွင် မျက်နှာပြင် ခွဲ၍ပြသခြင်းကို မပံ့ပိုးပါ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ဤအက်ပ်ကို ဝင်းဒိုး ၁ ခုတွင်သာ ဖွင့်နိုင်သည်။"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ဤအက်ပ်ကို ဝင်းဒိုး ၁ ခုတွင်သာ ဖွင့်နိုင်သည်"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ဤအက်ပ်အနေဖြင့် ဖွင့်ရန်စနစ်ကို ဒုတိယဖန်သားပြင်မှ အသုံးပြုရန် ပံ့ပိုးမထားပါ။"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း ပိုင်းခြားစနစ်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index cd656b846ccf..ec9ece3484b8 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Det kan hende at appen ikke fungerer med delt skjerm"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Appen støtter ikke delt skjerm"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne appen kan bare åpnes i ett vindu."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Denne appen kan bare åpnes i ett vindu"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skilleelement for delt skjerm"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 3300bc68c084..8bb07be12c48 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"यो एपले स्प्लिट स्क्रिन मोडमा काम नगर्न सक्छ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"यो एप स्प्लिट स्क्रिन मोडमा प्रयोग गर्न मिल्दैन"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"यो एप एउटा विन्डोमा मात्र खोल्न मिल्छ।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"यो एप एउटा विन्डोमा मात्र खोल्न मिल्छ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"स्प्लिट स्क्रिन डिभाइडर"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 0d174049dc15..c6c60ae4b1f2 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"De app werkt misschien niet met gesplitst scherm"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"App ondersteunt geen gesplitst scherm"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Deze app kan slechts in 1 venster worden geopend."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Deze app kan maar in 1 venster worden geopend"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Scheiding voor gesplitst scherm"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 916d8638436c..927dde40134d 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରେ ଆପ କାମ କରିନପାରେ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ଆପ ସମର୍ଥନ କରେ ନାହିଁ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ଏହି ଆପକୁ କେବଳ 1ଟି ୱିଣ୍ଡୋରେ ଖୋଲାଯାଇପାରିବ।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ଏହି ଆପକୁ କେବଳ 1ଟି ୱିଣ୍ଡୋରେ ଖୋଲାଯାଇପାରିବ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ ଲଞ୍ଚ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ଡିଭାଇଡର"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 5c936e069f9e..0e12fb872005 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ਐਪ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ਇਹ ਐਪ ਸਿਰਫ਼ 1 ਵਿੰਡੋ ਵਿੱਚ ਖੋਲ੍ਹੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ਇਹ ਐਪ ਸਿਰਫ਼ 1 ਵਿੰਡੋ ਵਿੱਚ ਖੋਲ੍ਹੀ ਜਾ ਸਕਦੀ ਹੈ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇਆਂ \'ਤੇ ਲਾਂਚ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿਭਾਜਕ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index abdb5f7eb7ac..75a8ce6bc16d 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacja może nie działać przy podzielonym ekranie"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacja nie obsługuje podzielonego ekranu"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ta aplikacja może być otwarta tylko w 1 oknie."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ta aplikacja może być otwarta tylko w 1 oknie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Linia dzielenia ekranu"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index bb68b28075bb..b84a0ded4939 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"É possível que o app não funcione com a tela dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"O app não oferece suporte à divisão de tela"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Este app só pode ser aberto em uma única janela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esse app só pode ser aberto em uma única janela"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de tela"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 2c03c543d1dc..d84bfcdd73ff 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"A app pode não funcionar com o ecrã dividido"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"A app não é compatível com o ecrã dividido"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app só pode ser aberta em 1 janela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta app só pode ser aberta em 1 janela"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A app não é compatível com o início em ecrãs secundários."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor do ecrã dividido"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index bb68b28075bb..b84a0ded4939 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"É possível que o app não funcione com a tela dividida"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"O app não oferece suporte à divisão de tela"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Este app só pode ser aberto em uma única janela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esse app só pode ser aberto em uma única janela"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de tela"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 579046b3a45f..eeea428cc8fa 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulează stocarea"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplicația nu acceptă ecranul împărțit"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplicația poate fi deschisă într-o singură fereastră."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aplicația se poate deschide într-o singură fereastră"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Separator pentru ecranul împărțit"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index a8a914cbf9fc..26b0f94eb585 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Когда включено разделение экрана, приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Приложение не поддерживает разделение экрана."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Это приложение можно открыть только в одном окне."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Это приложение можно открыть только в одном окне."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложение не поддерживает запуск на дополнительных экранах"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Разделитель экрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 968bc2c3b862..9b9a430ce73f 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"යෙදුම බෙදීම් තිරය සමග ක්‍රියා නොකළ හැක"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"යෙදුම බෙදුම් තිරයට සහාය නොදක්වයි"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"මෙම යෙදුම විවෘත කළ හැක්කේ 1 කවුළුවක පමණයි."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"මෙම යෙදුම විවෘත කළ හැක්කේ 1 කවුළුවක පමණයි"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"යෙදුම ද්විතීයික සංදර්ශක මත දියත් කිරීම සඳහා සහාය නොදක්වයි."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"බෙදුම් තිර වෙන්කරණය"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 303d81b17568..4b2153180cbe 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikácia nemusí fungovať s rozdelenou obrazovkou"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikácia nepodporuje rozdelenú obrazovku"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Táto aplikácia môže byť otvorená iba v jednom okne."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Táto aplikácia môže byť otvorená iba v jednom okne"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Rozdeľovač obrazovky"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 6178a1a9a489..581cf5b815c6 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"To aplikacijo je mogoče odpreti samo v enem oknu."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"To aplikacijo je mogoče odpreti samo v enem oknu"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdelilnik zaslonov"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index afccac292ebe..9dc7b7ebef99 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Aplikacioni mund të mos funksionojë me ekranin e ndarë"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikacioni nuk mbështet ekranin e ndarë"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ky aplikacion mund të hapet vetëm në 1 dritare."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ky aplikacion mund të hapet vetëm në 1 dritare"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Ndarësi i ekranit të ndarë"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 0e70a47f3fcb..cd532f79dd78 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Апликација не подржава подељени екран."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ова апликација може да се отвори само у једном прозору."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ова апликација може да се отвори само у једном прозору"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликација не подржава покретање на секундарним екранима."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Разделник подељеног екрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 4c22964e125f..386dda7e088d 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Appen kanske inte fungerar med delad skärm"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Appen har inte stöd för delad skärm"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denna app kan bara vara öppen i ett fönster."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Denna app kan bara vara öppen i ett fönster"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan inte köras på en sekundär skärm."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Avdelare för delad skärm"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 71aeb61538cd..69b2e34ada3c 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Huenda programu isifanye kazi kwenye skrini iliyogawanywa"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Programu haifanyi kazi kwenye skrini iliyogawanywa"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Programu hii inaweza kufunguliwa katika dirisha 1 pekee."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Programu hii inaweza kufunguliwa katika dirisha 1 pekee"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programu hii haiwezi kufunguliwa kwenye madirisha mengine."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Kitenganishi cha kugawa skrini"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index aab05daca0ae..037b5aba22f5 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"திரைப் பிரிப்புப் பயன்முறையில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"திரைப் பிரிப்புப் பயன்முறையை ஆப்ஸ் ஆதரிக்காது"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"இந்த ஆப்ஸை 1 சாளரத்தில் மட்டுமே திறக்க முடியும்."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"இந்த ஆப்ஸை 1 சாளரத்தில் மட்டுமே திறக்க முடியும்"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"இரண்டாம்நிலைத் திரைகளில் பயன்பாட்டைத் தொடங்க முடியாது."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"திரைப் பிரிப்பான்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 1e407bf255de..694ecb951210 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"స్ప్లిట్ స్క్రీన్‌తో యాప్ పని చేయకపోవచ్చు"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"యాప్‌లో స్ప్లిట్ స్క్రీన్‌కు సపోర్ట్ లేదు"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ఈ యాప్‌ను 1 విండోలో మాత్రమే తెరవవచ్చు."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ఈ యాప్‌ను 1 విండోలో మాత్రమే తెరవవచ్చు"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"స్ప్లిట్ స్క్రీన్ డివైడర్"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 77a9f4b3ec76..d4b6aff2ee7d 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"แอปอาจใช้ไม่ได้กับโหมดแยกหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"แอปไม่รองรับการแยกหน้าจอ"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"แอปนี้เปิดได้ใน 1 หน้าต่างเท่านั้น"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"แอปนี้เปิดได้ใน 1 หน้าต่างเท่านั้น"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"แอปไม่รองรับการเรียกใช้ในจอแสดงผลรอง"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"เส้นแยกหน้าจอ"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 757da92ef72c..db9303c0fd9c 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Posibleng hindi gumana sa split screen ang app"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Hindi sinusuportahan ng app ang split-screen"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Sa 1 window lang puwedeng buksan ang app na ito."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Sa 1 window lang puwedeng buksan ang app na ito"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divider ng split screen"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 9c4bf8df9308..818666c79973 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Uygulama bölünmüş ekranda çalışmayabilir"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Uygulama bölünmüş ekranı desteklemiyor."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu uygulama yalnızca 1 pencerede açılabilir."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Bu uygulama yalnızca 1 pencerede açılabilir"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Bölünmüş ekran ayırıcı"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 753fe29ebd41..85fb8e114476 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Додаток може не працювати в режимі розділення екрана"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Додаток не підтримує розділення екрана"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Цей додаток можна відкрити лише в одному вікні."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Цей додаток можна відкрити лише в одному вікні"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Додаток не підтримує запуск на додаткових екранах."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Розділювач екрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index fb0137658560..813870b134b4 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ایپ اسپلٹ اسکرین کو سپورٹ نہیں کرتی ہے"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"یہ ایپ صرف 1 ونڈو میں کھولی جا سکتی ہے۔"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"یہ ایپ صرف 1 ونڈو میں کھولی جا سکتی ہے"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"اسپلٹ اسکرین ڈیوائیڈر"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 81e63de9767c..7bcacbb93f1f 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Bu ilovada ekranni ikkiga ajratish rejimi ishlamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Bu ilovada ekranni ikkiga ajratish ishlamaydi."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu ilovani faqat 1 ta oynada ochish mumkin."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Bu ilovani faqat 1 ta oynada ochish mumkin"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Ekranni ikkiga ajratish chizigʻi"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 16bbd5eda23e..416dd91162c2 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Có thể ứng dụng không dùng được chế độ chia đôi màn hình"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Ứng dụng không hỗ trợ chế độ chia đôi màn hình"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ứng dụng này chỉ có thể mở 1 cửa sổ."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ứng dụng này chỉ có thể mở trong 1 cửa sổ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Trình chia đôi màn hì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 c12ec8423858..6ad172807f6a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"应用可能无法在分屏模式下正常运行"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"应用不支持分屏"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"此应用只能在 1 个窗口中打开。"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"此应用只能在 1 个窗口中打开"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"应用不支持在辅显示屏上启动。"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"分屏分隔线"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index c9543488688b..b5b94ec40fd1 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"應用程式可能無法在分割螢幕中運作"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"應用程式不支援分割螢幕"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"此應用程式只可在 1 個視窗中開啟"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"此應用程式只可在 1 個視窗中開啟"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示屏上啟動。"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"分割螢幕分隔線"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index d25bfd74ca04..0f2a052dbbe0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"應用程式可能無法在分割畫面中運作"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"這個應用程式不支援分割畫面"</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"這個應用程式只能在 1 個視窗中開啟。"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"這個應用程式只能在 1 個視窗中開啟"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示器上啟動。"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"分割畫面分隔線"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index bd62b65ccdf2..a696f9ec6251 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -34,7 +34,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="7429086980048964687">"Ama-app okungenzeka angasebenzi ngesikrini esihlukanisiwe"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"I-app ayisekeli isikrini esihlukanisiwe."</string>
- <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Le-app ingavulwa kuphela ewindini eli-1."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Le-app ingavulwa kuphela ewindini eli-1."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Isihlukanisi sokuhlukanisa isikrini"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 6a6f2b02766d..e4abae48c8fd 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -141,4 +141,7 @@
window. If false, the splash screen will be a solid color splash screen whenever the
app has not provided a windowSplashScreenAnimatedIcon. -->
<bool name="config_canUseAppIconForSplashScreen">true</bool>
+
+ <!-- Whether CompatUIController is enabled -->
+ <bool name="config_enableCompatUIController">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/config_tv.xml b/libs/WindowManager/Shell/res/values/config_tv.xml
new file mode 100644
index 000000000000..3da5539c9ae6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/config_tv.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <integer name="config_tvPipEnterFadeOutDuration">500</integer>
+ <integer name="config_tvPipEnterFadeInDuration">1500</integer>
+ <integer name="config_tvPipExitFadeOutDuration">500</integer>
+ <integer name="config_tvPipExitFadeInDuration">500</integer>
+</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index de9d2a292540..f20d44df21b1 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -224,9 +224,9 @@
<dimen name="bubbles_user_education_width">480dp</dimen>
<!-- Margin applied to the end of the user education views (really only matters for phone
since the width is match parent). -->
- <dimen name="bubble_user_education_margin_end">24dp</dimen>
+ <dimen name="bubble_user_education_margin_horizontal">24dp</dimen>
<!-- Padding applied to the end of the user education view. -->
- <dimen name="bubble_user_education_padding_end">58dp</dimen>
+ <dimen name="bubble_user_education_padding_horizontal">58dp</dimen>
<!-- Padding between the bubble and the user education text. -->
<dimen name="bubble_user_education_stack_padding">16dp</dimen>
<!-- Max width for the bubble popup view. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index ab61a48a715c..5143d419597b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -115,6 +115,19 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
b.setParent(sc);
}
+ /**
+ * Re-parents the provided surface to the leash of the provided display.
+ *
+ * @param displayId the display area to reparent to.
+ * @param sc the surface to be reparented.
+ * @param t a {@link SurfaceControl.Transaction} in which to reparent.
+ */
+ public void reparentToDisplayArea(int displayId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ final SurfaceControl displayAreaLeash = mLeashes.get(displayId);
+ t.reparent(sc, displayAreaLeash);
+ }
+
public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
final SurfaceControl sc = mLeashes.get(displayId);
if (sc == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 6cd1324c7d24..efa5a1a64ade 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -21,6 +21,7 @@ import static android.app.ActivityOptions.ANIM_CUSTOM;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -253,7 +254,8 @@ class ActivityEmbeddingAnimationSpec {
private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
- final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
+ final int type = getTransitionTypeFromInfo(info);
+ final Animation a = loadAttributeAnimation(type, info, change, WALLPAPER_TRANSITION_NONE,
mTransitionAnimation, false);
return a != null && a.getShowBackdrop();
}
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 f0da35df39ee..3e113276027e 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
@@ -553,8 +553,9 @@ public class BubbleController implements ConfigurationChangeListener,
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
*/
void hideCurrentInputMethod() {
+ int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
try {
- mBarService.hideCurrentInputMethodForBubbles();
+ mBarService.hideCurrentInputMethodForBubbles(displayId);
} catch (RemoteException e) {
e.printStackTrace();
}
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 144c456f8838..09ae84a50328 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
@@ -46,6 +46,12 @@ public class BubblePositioner {
? "BubblePositioner"
: BubbleDebugConfig.TAG_BUBBLES;
+ /** The screen edge the bubble stack is pinned to */
+ public enum StackPinnedEdge {
+ LEFT,
+ RIGHT
+ }
+
/** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
public static final int NUM_VISIBLE_WHEN_RESTING = 2;
/** Indicates a bubble's height should be the maximum available space. **/
@@ -694,6 +700,15 @@ public class BubblePositioner {
final boolean startOnLeft = isAppBubble
? layoutDirection == LAYOUT_DIRECTION_RTL
: layoutDirection != LAYOUT_DIRECTION_RTL;
+ return getStartPosition(startOnLeft ? StackPinnedEdge.LEFT : StackPinnedEdge.RIGHT);
+ }
+
+ /**
+ * The stack position to use if user education is being shown.
+ *
+ * @param stackPinnedEdge the screen edge the stack is pinned to.
+ */
+ public PointF getStartPosition(StackPinnedEdge stackPinnedEdge) {
final RectF allowableStackPositionRegion = getAllowableStackPositionRegion(
1 /* default starts with 1 bubble */);
if (isLargeScreen()) {
@@ -702,7 +717,7 @@ public class BubblePositioner {
final float desiredY = mScreenRect.height() / 2f - (mBubbleSize / 2f);
final float offset = desiredY / mScreenRect.height();
return new BubbleStackView.RelativeStackPosition(
- startOnLeft,
+ stackPinnedEdge == StackPinnedEdge.LEFT,
offset)
.getAbsolutePositionInRegion(allowableStackPositionRegion);
} else {
@@ -710,7 +725,7 @@ public class BubblePositioner {
R.dimen.bubble_stack_starting_offset_y);
// TODO: placement bug here because mPositionRect doesn't handle the overhanging edge
return new BubbleStackView.RelativeStackPosition(
- startOnLeft,
+ stackPinnedEdge == StackPinnedEdge.LEFT,
startingVerticalOffset / mPositionRect.height())
.getAbsolutePositionInRegion(allowableStackPositionRegion);
}
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 87461dcb0515..2cee675e83be 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
@@ -25,6 +25,8 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT;
+import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT;
import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
@@ -1296,6 +1298,9 @@ public class BubbleStackView extends FrameLayout
return shouldShow;
}
+ /**
+ * Show manage education if should show and was not showing before.
+ */
private void maybeShowManageEdu() {
if (!shouldShowManageEdu()) {
return;
@@ -1304,7 +1309,16 @@ public class BubbleStackView extends FrameLayout
mManageEduView = new ManageEducationView(mContext, mPositioner);
addView(mManageEduView);
}
- mManageEduView.show(mExpandedBubble.getExpandedView());
+ showManageEdu();
+ }
+
+ /**
+ * Show manage education if was not showing before.
+ */
+ private void showManageEdu() {
+ if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) return;
+ mManageEduView.show(mExpandedBubble.getExpandedView(),
+ mStackAnimationController.isStackOnLeftSide());
}
@VisibleForTesting
@@ -1350,10 +1364,21 @@ public class BubbleStackView extends FrameLayout
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
}
+ return showStackEdu();
+ }
+
+ /**
+ * @return true if education view for the collapsed stack was not showing before.
+ */
+ private boolean showStackEdu() {
+ // Stack appears on top of the education views
mBubbleContainer.bringToFront();
// Ensure the stack is in the correct spot
- mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
- return mStackEduView.show(mPositioner.getDefaultStartPosition());
+ PointF position = mPositioner.getStartPosition(
+ mStackAnimationController.isStackOnLeftSide() ? LEFT : RIGHT);
+ // Animate stack to the position
+ mStackAnimationController.springStackAfterFling(position.x, position.y);
+ return mStackEduView.show(position);
}
@VisibleForTesting
@@ -1367,16 +1392,13 @@ public class BubbleStackView extends FrameLayout
removeView(mStackEduView);
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
- mBubbleContainer.bringToFront(); // Stack appears on top of the stack education
- // Ensure the stack is in the correct spot
- mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
- mStackEduView.show(mPositioner.getDefaultStartPosition());
+ showStackEdu();
}
if (isManageEduVisible()) {
removeView(mManageEduView);
mManageEduView = new ManageEducationView(mContext, mPositioner);
addView(mManageEduView);
- mManageEduView.show(mExpandedBubble.getExpandedView());
+ showManageEdu();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 1b41f793311d..61e17c8ec459 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -19,6 +19,7 @@ import android.content.Context
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -32,10 +33,10 @@ import com.android.wm.shell.animation.Interpolators
* User education view to highlight the manage button that allows a user to configure the settings
* for the bubble. Shown only the first time a user expands a bubble.
*/
-class ManageEducationView constructor(context: Context, positioner: BubblePositioner)
- : LinearLayout(context) {
+class ManageEducationView(context: Context, positioner: BubblePositioner) : LinearLayout(context) {
- private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "ManageEducationView"
+ private val TAG =
+ if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "ManageEducationView"
else BubbleDebugConfig.TAG_BUBBLES
private val ANIMATE_DURATION: Long = 200
@@ -62,7 +63,7 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
override fun setLayoutDirection(layoutDirection: Int) {
super.setLayoutDirection(layoutDirection)
- setDrawableDirection()
+ setDrawableDirection(layoutDirection == LAYOUT_DIRECTION_LTR)
}
override fun onFinishInflate() {
@@ -71,8 +72,10 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
}
private fun setButtonColor() {
- val typedArray = mContext.obtainStyledAttributes(intArrayOf(
- com.android.internal.R.attr.colorAccentPrimary))
+ val typedArray =
+ mContext.obtainStyledAttributes(
+ intArrayOf(com.android.internal.R.attr.colorAccentPrimary)
+ )
val buttonColor = typedArray.getColor(0 /* index */, Color.TRANSPARENT)
typedArray.recycle()
@@ -81,11 +84,11 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
gotItButton.setBackgroundDrawable(ColorDrawable(buttonColor))
}
- private fun setDrawableDirection() {
+ private fun setDrawableDirection(isOnLeft: Boolean) {
manageView.setBackgroundResource(
- if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
- R.drawable.bubble_stack_user_education_bg_rtl
- else R.drawable.bubble_stack_user_education_bg)
+ if (isOnLeft) R.drawable.bubble_stack_user_education_bg
+ else R.drawable.bubble_stack_user_education_bg_rtl
+ )
}
/**
@@ -93,48 +96,31 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
* bubble stack is expanded for the first time.
*
* @param expandedView the expandedView the user education is shown on top of.
+ * @param isStackOnLeft the bubble stack position on the screen
*/
- fun show(expandedView: BubbleExpandedView) {
+ fun show(expandedView: BubbleExpandedView, isStackOnLeft: Boolean) {
setButtonColor()
if (visibility == VISIBLE) return
bubbleExpandedView = expandedView
expandedView.taskView?.setObscuredTouchRect(Rect(positioner.screenRect))
- layoutParams.width = if (positioner.isLargeScreen || positioner.isLandscape)
- context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
- else ViewGroup.LayoutParams.MATCH_PARENT
-
alpha = 0f
visibility = View.VISIBLE
expandedView.getManageButtonBoundsOnScreen(realManageButtonRect)
- val isRTL = mContext.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
- if (isRTL) {
- val rightPadding = positioner.screenRect.right - realManageButtonRect.right -
- expandedView.manageButtonMargin
- manageView.setPadding(manageView.paddingLeft, manageView.paddingTop,
- rightPadding, manageView.paddingBottom)
- } else {
- manageView.setPadding(realManageButtonRect.left - expandedView.manageButtonMargin,
- manageView.paddingTop, manageView.paddingRight, manageView.paddingBottom)
- }
+ layoutManageView(realManageButtonRect, expandedView.manageButtonMargin, isStackOnLeft)
+
post {
- manageButton
- .setOnClickListener {
- hide()
- expandedView.requireViewById<View>(R.id.manage_button).performClick()
- }
+ manageButton.setOnClickListener {
+ hide()
+ expandedView.requireViewById<View>(R.id.manage_button).performClick()
+ }
gotItButton.setOnClickListener { hide() }
setOnClickListener { hide() }
val offsetViewBounds = Rect()
manageButton.getDrawingRect(offsetViewBounds)
manageView.offsetDescendantRectToMyCoords(manageButton, offsetViewBounds)
- if (isRTL && (positioner.isLargeScreen || positioner.isLandscape)) {
- translationX = (positioner.screenRect.right - width).toFloat()
- } else {
- translationX = 0f
- }
translationY = (realManageButtonRect.top - offsetViewBounds.top).toFloat()
bringToFront()
animate()
@@ -145,6 +131,79 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
setShouldShow(false)
}
+ /**
+ * On tablet the user education is aligned to the left or to right side depending on where the
+ * stack is positioned when collapsed. On phone the user education follows the layout direction.
+ *
+ * @param manageButtonRect the manage button rect on the screen
+ * @param manageButtonMargin the manage button margin
+ * @param isStackOnLeft the bubble stack position on the screen
+ */
+ private fun layoutManageView(
+ manageButtonRect: Rect,
+ manageButtonMargin: Int,
+ isStackOnLeft: Boolean
+ ) {
+ val isLTR = resources.configuration.layoutDirection == LAYOUT_DIRECTION_LTR
+ val isPinnedLeft = if (positioner.isLargeScreen) isStackOnLeft else isLTR
+ val paddingHorizontal =
+ resources.getDimensionPixelSize(R.dimen.bubble_user_education_padding_horizontal)
+
+ // The user education view background image direction
+ setDrawableDirection(isPinnedLeft)
+
+ // The user education view layout gravity
+ gravity = if (isPinnedLeft) Gravity.LEFT else Gravity.RIGHT
+
+ // The user education view width
+ manageView.layoutParams.width =
+ when {
+ // Left-to-Right direction and the education is on the right side
+ isLTR && !isPinnedLeft ->
+ positioner.screenRect.right -
+ (manageButtonRect.left - manageButtonMargin - paddingHorizontal)
+ // Right-to-Left direction and the education is on the left side
+ !isLTR && isPinnedLeft ->
+ manageButtonRect.right + manageButtonMargin + paddingHorizontal
+ // Large screen and the education position matches the layout direction
+ positioner.isLargeScreen -> ViewGroup.LayoutParams.WRAP_CONTENT
+ // Small screen, landscape orientation
+ positioner.isLandscape ->
+ resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
+ // Otherwise
+ else -> ViewGroup.LayoutParams.MATCH_PARENT
+ }
+
+ // The user education view margin on the opposite side of where it's pinned
+ (manageView.layoutParams as MarginLayoutParams).apply {
+ val edgeMargin =
+ resources.getDimensionPixelSize(R.dimen.bubble_user_education_margin_horizontal)
+ leftMargin = if (isPinnedLeft) 0 else edgeMargin
+ rightMargin = if (isPinnedLeft) edgeMargin else 0
+ }
+
+ // The user education view padding
+ manageView.apply {
+ val paddingLeft =
+ if (isLTR && isPinnedLeft) {
+ // Offset on the left to align with the manage button
+ manageButtonRect.left - manageButtonMargin
+ } else {
+ // Use default padding
+ paddingHorizontal
+ }
+ val paddingRight =
+ if (!isLTR && !isPinnedLeft) {
+ // Offset on the right to align with the manage button
+ positioner.screenRect.right - manageButtonRect.right - manageButtonMargin
+ } else {
+ // Use default padding
+ paddingHorizontal
+ }
+ setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
+ }
+ }
+
fun hide() {
bubbleExpandedView?.taskView?.setObscuredTouchRect(null)
if (visibility != VISIBLE || isHiding) return
@@ -160,9 +219,12 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
}
private fun setShouldShow(shouldShow: Boolean) {
- context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
- .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply()
+ context
+ .getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(PREF_MANAGED_EDUCATION, !shouldShow)
+ .apply()
}
}
-const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding" \ No newline at end of file
+const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 5e3a077a3716..2cabb65abe7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -21,7 +21,6 @@ import android.graphics.PointF
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
-import android.view.View.OnKeyListener
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
@@ -30,16 +29,17 @@ import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators
/**
- * User education view to highlight the collapsed stack of bubbles.
- * Shown only the first time a user taps the stack.
+ * User education view to highlight the collapsed stack of bubbles. Shown only the first time a user
+ * taps the stack.
*/
-class StackEducationView constructor(
+class StackEducationView(
context: Context,
positioner: BubblePositioner,
controller: BubbleController
) : LinearLayout(context) {
- private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
+ private val TAG =
+ if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
else BubbleDebugConfig.TAG_BUBBLES
private val ANIMATE_DURATION: Long = 200
@@ -69,7 +69,7 @@ class StackEducationView constructor(
override fun setLayoutDirection(layoutDirection: Int) {
super.setLayoutDirection(layoutDirection)
- setDrawableDirection()
+ setDrawableDirection(layoutDirection == LAYOUT_DIRECTION_LTR)
}
override fun onFinishInflate() {
@@ -111,16 +111,16 @@ class StackEducationView constructor(
descTextView.setTextColor(textColor)
}
- private fun setDrawableDirection() {
+ private fun setDrawableDirection(isOnLeft: Boolean) {
view.setBackgroundResource(
- if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR)
- R.drawable.bubble_stack_user_education_bg
- else R.drawable.bubble_stack_user_education_bg_rtl)
+ if (isOnLeft) R.drawable.bubble_stack_user_education_bg
+ else R.drawable.bubble_stack_user_education_bg_rtl
+ )
}
/**
- * If necessary, shows the user education view for the bubble stack. This appears the first
- * time a user taps on a bubble.
+ * If necessary, shows the user education view for the bubble stack. This appears the first time
+ * a user taps on a bubble.
*
* @return true if user education was shown and wasn't showing before, false otherwise.
*/
@@ -129,29 +129,44 @@ class StackEducationView constructor(
if (visibility == VISIBLE) return false
controller.updateWindowFlagsForBackpress(true /* interceptBack */)
- layoutParams.width = if (positioner.isLargeScreen || positioner.isLandscape)
- context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
- else ViewGroup.LayoutParams.MATCH_PARENT
+ layoutParams.width =
+ if (positioner.isLargeScreen || positioner.isLandscape)
+ context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
+ else ViewGroup.LayoutParams.MATCH_PARENT
+
+ val isStackOnLeft = positioner.isStackOnLeft(stackPosition)
+ (view.layoutParams as MarginLayoutParams).apply {
+ // Update the horizontal margins depending on the stack position
+ val edgeMargin =
+ resources.getDimensionPixelSize(R.dimen.bubble_user_education_margin_horizontal)
+ leftMargin = if (isStackOnLeft) 0 else edgeMargin
+ rightMargin = if (isStackOnLeft) edgeMargin else 0
+ }
- val stackPadding = context.resources.getDimensionPixelSize(
- R.dimen.bubble_user_education_stack_padding)
+ val stackPadding =
+ context.resources.getDimensionPixelSize(R.dimen.bubble_user_education_stack_padding)
setAlpha(0f)
setVisibility(View.VISIBLE)
+ setDrawableDirection(isOnLeft = isStackOnLeft)
post {
requestFocus()
with(view) {
- if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
- setPadding(positioner.bubbleSize + stackPadding, paddingTop, paddingRight,
- paddingBottom)
+ if (isStackOnLeft) {
+ setPadding(
+ positioner.bubbleSize + stackPadding,
+ paddingTop,
+ paddingRight,
+ paddingBottom
+ )
+ translationX = 0f
} else {
- setPadding(paddingLeft, paddingTop, positioner.bubbleSize + stackPadding,
- paddingBottom)
- if (positioner.isLargeScreen || positioner.isLandscape) {
- translationX = (positioner.screenRect.right - width - stackPadding)
- .toFloat()
- } else {
- translationX = 0f
- }
+ setPadding(
+ paddingLeft,
+ paddingTop,
+ positioner.bubbleSize + stackPadding,
+ paddingBottom
+ )
+ translationX = (positioner.screenRect.right - width - stackPadding).toFloat()
}
translationY = stackPosition.y + positioner.bubbleSize / 2 - getHeight() / 2
}
@@ -168,7 +183,7 @@ class StackEducationView constructor(
* If necessary, hides the stack education view.
*
* @param isExpanding if true this indicates the hide is happening due to the bubble being
- * expanded, false if due to a touch outside of the bubble stack.
+ * expanded, false if due to a touch outside of the bubble stack.
*/
fun hide(isExpanding: Boolean) {
if (visibility != VISIBLE || isHiding) return
@@ -182,9 +197,12 @@ class StackEducationView constructor(
}
private fun setShouldShow(shouldShow: Boolean) {
- context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
- .edit().putBoolean(PREF_STACK_EDUCATION, !shouldShow).apply()
+ context
+ .getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(PREF_STACK_EDUCATION, !shouldShow)
+ .apply()
}
}
-const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding" \ No newline at end of file
+const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index e9344ffcce0c..1c74f415ec90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -322,13 +322,12 @@ public class SystemWindows {
}
@Override
- public void remove(android.view.IWindow window) throws RemoteException {
- super.remove(window);
+ public void remove(IBinder clientToken) throws RemoteException {
+ super.remove(clientToken);
synchronized(this) {
- IBinder token = window.asBinder();
- new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
+ new SurfaceControl.Transaction().remove(mLeashForWindow.get(clientToken))
.apply();
- mLeashForWindow.remove(token);
+ mLeashForWindow.remove(clientToken);
}
}
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 d520ff791e07..8b6c7b663f82 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
@@ -258,7 +258,7 @@ public class PipBoundsState {
ActivityTaskManager.getService().onPictureInPictureStateChanged(
new PictureInPictureUiState(stashedState != STASH_TYPE_NONE /* isStashed */)
);
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Unable to set alert PiP state change.", TAG);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 108aa8275009..1e30d8feb132 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -21,13 +21,13 @@ import android.app.WindowConfiguration
import android.content.ComponentName
import android.content.Context
import android.os.RemoteException
-import android.os.SystemProperties
import android.util.DisplayMetrics
import android.util.Log
import android.util.Pair
import android.util.TypedValue
import android.window.TaskSnapshot
import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.Flags
import com.android.wm.shell.protolog.ShellProtoLogGroup
import kotlin.math.abs
@@ -37,7 +37,6 @@ object PipUtils {
// Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
private const val EPSILON = 1e-7
- private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation"
/**
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
@@ -138,5 +137,5 @@ object PipUtils {
@JvmStatic
val isPip2ExperimentEnabled: Boolean
- get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false)
+ get() = Flags.enablePip2Implementation()
} \ No newline at end of file
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 54cf84c32276..27dc870e81ae 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
@@ -221,34 +221,57 @@ public abstract class WMShellBaseModule {
Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
- CompatUIController compatUI,
+ Optional<CompatUIController> compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
+ @ShellMainThread ShellExecutor mainExecutor) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTaskOrganizerOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
- return new ShellTaskOrganizer(shellInit, shellCommandHandler, compatUI,
- unfoldAnimationController, recentTasksOptional, mainExecutor);
+ return new ShellTaskOrganizer(
+ shellInit,
+ shellCommandHandler,
+ compatUI.orElse(null),
+ unfoldAnimationController,
+ recentTasksOptional,
+ mainExecutor);
}
@WMSingleton
@Provides
- static CompatUIController provideCompatUIController(Context context,
+ static Optional<CompatUIController> provideCompatUIController(
+ Context context,
ShellInit shellInit,
ShellController shellController,
- DisplayController displayController, DisplayInsetsController displayInsetsController,
- DisplayImeController imeController, SyncTransactionQueue syncQueue,
- @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
- DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ DisplayImeController imeController,
+ SyncTransactionQueue syncQueue,
+ @ShellMainThread ShellExecutor mainExecutor,
+ Lazy<Transitions> transitionsLazy,
+ DockStateReader dockStateReader,
+ CompatUIConfiguration compatUIConfiguration,
CompatUIShellCommandHandler compatUIShellCommandHandler,
AccessibilityManager accessibilityManager) {
- return new CompatUIController(context, shellInit, shellController, displayController,
- displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
- dockStateReader, compatUIConfiguration, compatUIShellCommandHandler,
- accessibilityManager);
+ if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ new CompatUIController(
+ context,
+ shellInit,
+ shellController,
+ displayController,
+ displayInsetsController,
+ imeController,
+ syncQueue,
+ mainExecutor,
+ transitionsLazy,
+ dockStateReader,
+ compatUIConfiguration,
+ compatUIShellCommandHandler,
+ accessibilityManager));
}
@WMSingleton
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 47769a8eeb11..71bf487249fb 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
@@ -59,6 +59,7 @@ import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
@@ -498,6 +499,7 @@ public abstract class WMShellModule {
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+ DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
RecentsTransitionHandler recentsTransitionHandler,
@@ -506,8 +508,19 @@ public abstract class WMShellModule {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
- toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
- launchAdjacentController, recentsTransitionHandler, mainExecutor);
+ toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler,
+ desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler,
+ mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
+ Context context,
+ Transitions transitions,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ return new DragToDesktopTransitionHandler(context, transitions,
+ rootTaskDisplayAreaOrganizer);
}
@WMSingleton
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 a9675f976fa9..1947097c2f15 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
@@ -20,6 +20,8 @@ import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
+import androidx.annotation.NonNull;
+
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -41,7 +43,6 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
@@ -78,11 +79,12 @@ public abstract class TvPipModule {
PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
+ PipTransitionState pipTransitionState,
PipAppOpsListener pipAppOpsListener,
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
- PipTransitionController pipTransitionController,
+ TvPipTransition tvPipTransition,
TvPipNotificationController tvPipNotificationController,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
@@ -99,9 +101,10 @@ public abstract class TvPipModule {
pipDisplayLayoutState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
+ pipTransitionState,
pipAppOpsListener,
pipTaskOrganizer,
- pipTransitionController,
+ tvPipTransition,
tvPipMenuController,
pipMediaController,
tvPipNotificationController,
@@ -151,25 +154,23 @@ public abstract class TvPipModule {
return new LegacySizeSpecSource(context, pipDisplayLayoutState);
}
- // Handler needed for loadDrawableAsync() in PipControlsViewController
@WMSingleton
@Provides
- static PipTransitionController provideTvPipTransition(
+ static TvPipTransition provideTvPipTransition(
Context context,
- ShellInit shellInit,
- ShellTaskOrganizer shellTaskOrganizer,
- Transitions transitions,
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
TvPipBoundsState tvPipBoundsState,
- PipDisplayLayoutState pipDisplayLayoutState,
- PipTransitionState pipTransitionState,
- TvPipMenuController pipMenuController,
+ TvPipMenuController tvPipMenuController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
+ PipTransitionState pipTransitionState,
PipAnimationController pipAnimationController,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipDisplayLayoutState pipDisplayLayoutState) {
return new TvPipTransition(context, shellInit, shellTaskOrganizer, transitions,
- tvPipBoundsState, pipDisplayLayoutState, pipTransitionState, pipMenuController,
- tvPipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
- Optional.empty());
+ tvPipBoundsState, tvPipMenuController, tvPipBoundsAlgorithm, pipTransitionState,
+ pipAnimationController, pipSurfaceTransactionHelper, pipDisplayLayoutState);
}
@WMSingleton
@@ -207,7 +208,7 @@ public abstract class TvPipModule {
PipTransitionState pipTransitionState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
- PipTransitionController pipTransitionController,
+ TvPipTransition tvPipTransition,
PipParamsChangedForwarder pipParamsChangedForwarder,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenControllerOptional,
@@ -217,7 +218,7 @@ public abstract class TvPipModule {
return new TvPipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
- pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+ pipSurfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
splitScreenControllerOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
}
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 8e12991080ed..4a9ea6fed73f 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
@@ -59,6 +59,7 @@ 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.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
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
@@ -92,6 +93,7 @@ class DesktopTasksController(
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
private val toggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler,
+ private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
@@ -110,6 +112,20 @@ class DesktopTasksController(
launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks
}
}
+ private val dragToDesktopStateListener = object : DragToDesktopStateListener {
+ override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ private fun removeVisualIndicator(tx: SurfaceControl.Transaction) {
+ visualIndicator?.releaseVisualIndicator(tx)
+ visualIndicator = null
+ }
+ }
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
@@ -122,9 +138,7 @@ class DesktopTasksController(
)
private var recentsAnimationRunning = false
-
- // This is public to avoid cyclic dependency; it is set by SplitScreenController
- lateinit var splitScreenController: SplitScreenController
+ private lateinit var splitScreenController: SplitScreenController
init {
desktopMode = DesktopModeImpl()
@@ -143,7 +157,7 @@ class DesktopTasksController(
)
transitions.addHandler(this)
desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
-
+ dragToDesktopTransitionHandler.setDragToDesktopStateListener(dragToDesktopStateListener)
recentsTransitionHandler.addTransitionStateListener(
object : RecentsTransitionStateListener {
override fun onAnimationStateChanged(running: Boolean) {
@@ -158,6 +172,12 @@ class DesktopTasksController(
)
}
+ /** Setter needed to avoid cyclic dependency. */
+ fun setSplitScreenController(controller: SplitScreenController) {
+ splitScreenController = controller
+ dragToDesktopTransitionHandler.setSplitScreenController(controller)
+ }
+
/** Show all tasks, that are part of the desktop, on top of launcher */
fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
@@ -248,56 +268,43 @@ class DesktopTasksController(
}
/**
- * The first part of the animated move to desktop transition. Applies the changes to move task
- * to desktop mode and sets the taskBounds to the passed in bounds, startBounds. This is
- * followed with a call to {@link finishMoveToDesktop} or {@link cancelMoveToDesktop}.
+ * The first part of the animated drag to desktop transition. This is
+ * followed with a call to [finalizeDragToDesktop] or [cancelDragToDesktop].
*/
- fun startMoveToDesktop(
+ fun startDragToDesktop(
taskInfo: RunningTaskInfo,
- startBounds: Rect,
- dragToDesktopValueAnimator: MoveToDesktopAnimator
+ dragToDesktopValueAnimator: MoveToDesktopAnimator,
+ windowDecor: DesktopModeWindowDecoration
) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: startMoveToDesktop taskId=%d",
- taskInfo.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: startDragToDesktop taskId=%d",
+ taskInfo.taskId
+ )
+ dragToDesktopTransitionHandler.startDragToDesktopTransition(
+ taskInfo.taskId,
+ dragToDesktopValueAnimator,
+ windowDecor
)
- val wct = WindowContainerTransaction()
- exitSplitIfApplicable(wct, taskInfo)
- moveHomeTaskToFront(wct)
- addMoveToDesktopChanges(wct, taskInfo)
- wct.setBounds(taskInfo.token, startBounds)
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startMoveToDesktop(wct, dragToDesktopValueAnimator,
- mOnAnimationFinishedCallback)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
- }
}
/**
- * The second part of the animated move to desktop transition, called after
- * {@link startMoveToDesktop}. Brings apps to front and sets freeform task bounds.
+ * The second part of the animated drag to desktop transition, called after
+ * [startDragToDesktop].
*/
- private fun finalizeMoveToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
+ private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: finalizeMoveToDesktop taskId=%d",
- taskInfo.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: finalizeDragToDesktop taskId=%d",
+ taskInfo.taskId
)
val wct = WindowContainerTransaction()
+ exitSplitIfApplicable(wct, taskInfo)
+ moveHomeTaskToFront(wct)
bringDesktopAppsToFront(taskInfo.displayId, wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct,
- mOnAnimationFinishedCallback)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
- releaseVisualIndicator()
- }
+ dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
}
/**
@@ -353,40 +360,40 @@ class DesktopTasksController(
}
private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
- if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
- splitScreenController.prepareExitSplitScreen(wct,
- splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+ if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) {
+ splitScreenController.prepareExitSplitScreen(
+ wct,
+ splitScreenController.getStageOfTask(taskInfo.taskId),
+ EXIT_REASON_ENTER_DESKTOP
+ )
+ getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
+ wct.removeTask(otherTaskInfo.token)
+ }
}
}
+ private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? {
+ val remainingTaskPosition: Int =
+ if (splitScreenController.getSplitPosition(taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ SPLIT_POSITION_TOP_OR_LEFT
+ } else {
+ SPLIT_POSITION_BOTTOM_OR_RIGHT
+ }
+ return splitScreenController.getTaskInfo(remainingTaskPosition)
+ }
+
/**
- * The second part of the animated move to desktop transition, called after
- * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
- * and released back into status bar area.
+ * The second part of the animated drag to desktop transition, called after
+ * [startDragToDesktop].
*/
- fun cancelMoveToDesktop(task: RunningTaskInfo, moveToDesktopAnimator: MoveToDesktopAnimator) {
+ fun cancelDragToDesktop(task: RunningTaskInfo) {
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: cancelMoveToDesktop taskId=%d",
+ "DesktopTasksController: cancelDragToDesktop taskId=%d",
task.taskId
)
- val wct = WindowContainerTransaction()
- wct.setBounds(task.token, Rect())
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct,
- moveToDesktopAnimator) { t ->
- val callbackWCT = WindowContainerTransaction()
- visualIndicator?.releaseVisualIndicator(t)
- visualIndicator = null
- addMoveToFullscreenChanges(callbackWCT, task)
- transitions.startTransition(TRANSIT_CHANGE, callbackWCT, null /* handler */)
- }
- } else {
- addMoveToFullscreenChanges(wct, task)
- shellTaskOrganizer.applyTransaction(wct)
- releaseVisualIndicator()
- }
+ dragToDesktopTransitionHandler.cancelDragToDesktopTransition()
}
private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) {
@@ -966,6 +973,11 @@ class DesktopTasksController(
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR)
+ // TODO(b/301106941): don't show the indicator until the drag-to-desktop animation has
+ // started, or it'll be visible too early on top of the task surface, especially in
+ // the cancel-early case. Also because it shouldn't even be shown in the cancel-early
+ // case since its dismissal is tied to the cancel animation end, which doesn't even run
+ // in cancel-early.
visualIndicator?.createIndicatorWithAnimatedBounds()
}
val indicator = visualIndicator ?: return
@@ -988,7 +1000,7 @@ class DesktopTasksController(
taskInfo: RunningTaskInfo,
freeformBounds: Rect
) {
- finalizeMoveToDesktop(taskInfo, freeformBounds)
+ finalizeDragToDesktop(taskInfo, freeformBounds)
}
private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
new file mode 100644
index 000000000000..75d27d99b9ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -0,0 +1,543 @@
+package com.android.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.app.ActivityOptions
+import android.app.ActivityOptions.SourceInfo
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.FILL_IN_COMPONENT
+import android.graphics.Rect
+import android.os.IBinder
+import android.os.SystemClock
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TransitionHandler
+import com.android.wm.shell.util.TransitionUtil
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator.Companion.DRAG_FREEFORM_SCALE
+import java.util.function.Supplier
+
+/**
+ * Handles the transition to enter desktop from fullscreen by dragging on the handle bar. It also
+ * handles the cancellation case where the task is dragged back to the status bar area in the same
+ * gesture.
+ */
+class DragToDesktopTransitionHandler(
+ private val context: Context,
+ private val transitions: Transitions,
+ private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+) : TransitionHandler {
+
+ constructor(
+ context: Context,
+ transitions: Transitions,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ ) : this(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ Supplier { SurfaceControl.Transaction() }
+ )
+
+ private val rectEvaluator = RectEvaluator(Rect())
+ private val launchHomeIntent = Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+
+ private var dragToDesktopStateListener: DragToDesktopStateListener? = null
+ private var splitScreenController: SplitScreenController? = null
+ private var transitionState: TransitionState? = null
+
+ /** Sets a listener to receive callback about events during the transition animation. */
+ fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
+ dragToDesktopStateListener = listener
+ }
+
+ /** Setter needed to avoid cyclic dependency. */
+ fun setSplitScreenController(controller: SplitScreenController) {
+ splitScreenController = controller
+ }
+
+ /**
+ * Starts a transition that performs a transient launch of Home so that Home is brought to the
+ * front while still keeping the currently focused task that is being dragged resumed. This
+ * allows the animation handler to reorder the task to the front and to scale it with the
+ * gesture into the desktop area with the Home and wallpaper behind it.
+ *
+ * Note that the transition handler for this transition doesn't call the finish callback until
+ * after one of the "end" or "cancel" transitions is merged into this transition.
+ */
+ fun startDragToDesktopTransition(
+ taskId: Int,
+ dragToDesktopAnimator: MoveToDesktopAnimator,
+ windowDecoration: DesktopModeWindowDecoration
+ ) {
+ if (transitionState != null) {
+ error("A drag to desktop is already in progress")
+ }
+
+ val options = ActivityOptions.makeBasic().apply {
+ setTransientLaunch()
+ setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode */,
+ launchHomeIntent,
+ FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT
+ )
+ val wct = WindowContainerTransaction()
+ wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
+ val startTransitionToken = transitions
+ .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
+
+ transitionState = if (isSplitTask(taskId)) {
+ TransitionState.FromSplit(
+ draggedTaskId = taskId,
+ dragAnimator = dragToDesktopAnimator,
+ windowDecoration = windowDecoration,
+ startTransitionToken = startTransitionToken
+ )
+ } else {
+ TransitionState.FromFullscreen(
+ draggedTaskId = taskId,
+ dragAnimator = dragToDesktopAnimator,
+ windowDecoration = windowDecoration,
+ startTransitionToken = startTransitionToken
+ )
+ }
+ }
+
+ /**
+ * Starts a transition that "finishes" the drag to desktop gesture. This transition is intended
+ * to merge into the "start" transition and is the one that actually applies the bounds and
+ * windowing mode changes to the dragged task. This is called when the dragged task is released
+ * inside the desktop drop zone.
+ */
+ fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+ transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+ }
+
+ /**
+ * Starts a transition that "cancels" the drag to desktop gesture. This transition is intended
+ * to merge into the "start" transition and it restores the transient state that was used to
+ * launch the Home task over the dragged task. This is called when the dragged task is released
+ * outside the desktop drop zone and is instead dropped back into the status bar region that
+ * means the user wants to remain in their current windowing mode.
+ */
+ fun cancelDragToDesktopTransition() {
+ val state = requireTransitionState()
+ state.cancelled = true
+ if (state.draggedTaskChange != null) {
+ // Regular case, transient launch of Home happened as is waiting for the cancel
+ // transient to start and merge. Animate the cancellation (scale back to original
+ // bounds) first before actually starting the cancel transition so that the wallpaper
+ // is visible behind the animating task.
+ startCancelAnimation()
+ } else {
+ // There's no dragged task, this can happen when the "cancel" happened too quickly
+ // before the "start" transition is even ready (like on a fling gesture). The
+ // "shrink" animation didn't even start, so there's no need to animate the "cancel".
+ // We also don't want to start the cancel transition yet since we don't have
+ // enough info to restore the order. We'll check for the cancelled state flag when
+ // the "start" animation is ready and cancel from #startAnimation instead.
+ }
+ }
+
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback
+ ): Boolean {
+ val state = requireTransitionState()
+
+ val isStartDragToDesktop = info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
+ transition == state.startTransitionToken
+ if (!isStartDragToDesktop) {
+ return false
+ }
+
+ // Layering: non-wallpaper, non-home tasks excluding the dragged task go at the bottom,
+ // then Home on top of that, wallpaper on top of that and finally the dragged task on top
+ // of everything.
+ val appLayers = info.changes.size
+ val homeLayers = info.changes.size * 2
+ val wallpaperLayers = info.changes.size * 3
+ val dragLayer = wallpaperLayers
+ val leafTaskFilter = TransitionUtil.LeafTaskFilter()
+ info.changes.withIndex().forEach { (i, change) ->
+ if (TransitionUtil.isWallpaper(change)) {
+ val layer = wallpaperLayers - i
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ } else if (isHomeChange(change)) {
+ state.homeToken = change.container
+ val layer = homeLayers - i
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ } else if (TransitionInfo.isIndependent(change, info)) {
+ // Root.
+ when (state) {
+ is TransitionState.FromSplit -> {
+ state.splitRootChange = change
+ val layer = if (!state.cancelled) {
+ // Normal case, split root goes to the bottom behind everything else.
+ appLayers - i
+ } else {
+ // Cancel-early case, pretend nothing happened so split root stays top.
+ dragLayer
+ }
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ }
+ is TransitionState.FromFullscreen -> {
+ if (change.taskInfo?.taskId == state.draggedTaskId) {
+ state.draggedTaskChange = change
+ val bounds = change.endAbsBounds
+ startTransaction.apply {
+ setLayer(change.leash, dragLayer)
+ setWindowCrop(change.leash, bounds.width(), bounds.height())
+ show(change.leash)
+ }
+ } else {
+ throw IllegalStateException("Expected root to be dragged task")
+ }
+ }
+ }
+ } else if (leafTaskFilter.test(change)) {
+ // When dragging one of the split tasks, the dragged leaf needs to be re-parented
+ // so that it can be layered separately from the rest of the split root/stages.
+ // The split root including the other split side was layered behind the wallpaper
+ // and home while the dragged split needs to be layered in front of them.
+ // Do not do this in the cancel-early case though, since in that case nothing should
+ // happen on screen so the layering will remain the same as if no transition
+ // occurred.
+ if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) {
+ state.draggedTaskChange = change
+ taskDisplayAreaOrganizer.reparentToDisplayArea(
+ change.endDisplayId, change.leash, startTransaction)
+ val bounds = change.endAbsBounds
+ startTransaction.apply {
+ setLayer(change.leash, dragLayer)
+ setWindowCrop(change.leash, bounds.width(), bounds.height())
+ show(change.leash)
+ }
+ }
+ }
+ }
+ state.startTransitionFinishCb = finishCallback
+ state.startTransitionFinishTransaction = finishTransaction
+ startTransaction.apply()
+
+ if (!state.cancelled) {
+ // Normal case, start animation to scale down the dragged task. It'll also be moved to
+ // follow the finger and when released we'll start the next phase/transition.
+ state.dragAnimator.startAnimation()
+ } else {
+ // Cancel-early case, the state was flagged was cancelled already, which means the
+ // gesture ended in the cancel region. This can happen even before the start transition
+ // is ready/animate here when cancelling quickly like with a fling. There's no point
+ // in starting the scale down animation that we would scale up anyway, so just jump
+ // directly into starting the cancel transition to restore WM order. Surfaces should
+ // not move as if no transition happened.
+ startCancelDragToDesktopTransition()
+ }
+ return true
+ }
+
+ override fun mergeAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ mergeTarget: IBinder,
+ finishCallback: Transitions.TransitionFinishCallback
+ ) {
+ val state = requireTransitionState()
+ val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
+ transition == state.cancelTransitionToken &&
+ mergeTarget == state.startTransitionToken
+ val isEndTransition = info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
+ mergeTarget == state.startTransitionToken
+
+ val startTransactionFinishT = state.startTransitionFinishTransaction
+ ?: error("Start transition expected to be waiting for merge but wasn't")
+ val startTransitionFinishCb = state.startTransitionFinishCb
+ ?: error("Start transition expected to be waiting for merge but wasn't")
+ if (isEndTransition) {
+ info.changes.withIndex().forEach { (i, change) ->
+ if (change.mode == TRANSIT_CLOSE) {
+ t.hide(change.leash)
+ startTransactionFinishT.hide(change.leash)
+ } else if (change.taskInfo?.taskId == state.draggedTaskId) {
+ t.show(change.leash)
+ startTransactionFinishT.show(change.leash)
+ state.draggedTaskChange = change
+ } else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) {
+ // Other freeform tasks that are being restored go behind the dragged task.
+ val draggedTaskLeash = state.draggedTaskChange?.leash
+ ?: error("Expected dragged leash to be non-null")
+ t.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+ startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+ }
+ }
+
+ val draggedTaskChange = state.draggedTaskChange
+ ?: throw IllegalStateException("Expected non-null change of dragged task")
+ val draggedTaskLeash = draggedTaskChange.leash
+ val startBounds = draggedTaskChange.startAbsBounds
+ val endBounds = draggedTaskChange.endAbsBounds
+
+ // TODO(b/301106941): Instead of forcing-finishing the animation that scales the
+ // surface down and then starting another that scales it back up to the final size,
+ // blend the two animations.
+ state.dragAnimator.endAnimator()
+ // Using [DRAG_FREEFORM_SCALE] to calculate animated width/height is possible because
+ // it is known that the animation scale is finished because the animation was
+ // force-ended above. This won't be true when the two animations are blended.
+ val animStartWidth = (startBounds.width() * DRAG_FREEFORM_SCALE).toInt()
+ val animStartHeight = (startBounds.height() * DRAG_FREEFORM_SCALE).toInt()
+ // Using end bounds here to find the left/top also assumes the center animation has
+ // finished and the surface is placed exactly in the center of the screen which matches
+ // the end/default bounds of the now freeform task.
+ val animStartLeft = endBounds.centerX() - (animStartWidth / 2)
+ val animStartTop = endBounds.centerY() - (animStartHeight / 2)
+ val animStartBounds = Rect(
+ animStartLeft,
+ animStartTop,
+ animStartLeft + animStartWidth,
+ animStartTop + animStartHeight
+ )
+
+
+ dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t)
+ t.apply {
+ setScale(draggedTaskLeash, 1f, 1f)
+ setPosition(
+ draggedTaskLeash,
+ animStartBounds.left.toFloat(),
+ animStartBounds.top.toFloat()
+ )
+ setWindowCrop(
+ draggedTaskLeash,
+ animStartBounds.width(),
+ animStartBounds.height()
+ )
+ }
+ // Accept the merge by applying the merging transaction (applied by #showResizeVeil)
+ // and finish callback. Show the veil and position the task at the first frame before
+ // starting the final animation.
+ state.windowDecoration.showResizeVeil(t, animStartBounds)
+ finishCallback.onTransitionFinished(null /* wct */)
+
+ // Because the task surface was scaled down during the drag, we must use the animated
+ // bounds instead of the [startAbsBounds].
+ val tx: SurfaceControl.Transaction = transactionSupplier.get()
+ ValueAnimator.ofObject(rectEvaluator, animStartBounds, endBounds)
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val animBounds = animator.animatedValue as Rect
+ tx.apply {
+ setScale(draggedTaskLeash, 1f, 1f)
+ setPosition(
+ draggedTaskLeash,
+ animBounds.left.toFloat(),
+ animBounds.top.toFloat()
+ )
+ setWindowCrop(
+ draggedTaskLeash,
+ animBounds.width(),
+ animBounds.height()
+ )
+ }
+ state.windowDecoration.updateResizeVeil(tx, animBounds)
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ state.windowDecoration.hideResizeVeil()
+ startTransitionFinishCb.onTransitionFinished(null /* null */)
+ clearState()
+ }
+ })
+ start()
+ }
+ } else if (isCancelTransition) {
+ info.changes.forEach { change ->
+ t.show(change.leash)
+ startTransactionFinishT.show(change.leash)
+ }
+ t.apply()
+ finishCallback.onTransitionFinished(null /* wct */)
+ startTransitionFinishCb.onTransitionFinished(null /* wct */)
+ clearState()
+ }
+ }
+
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo
+ ): WindowContainerTransaction? {
+ // Only handle transitions started from shell.
+ return null
+ }
+
+ private fun isHomeChange(change: Change): Boolean {
+ return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
+ }
+
+ private fun startCancelAnimation() {
+ val state = requireTransitionState()
+ val dragToDesktopAnimator = state.dragAnimator
+
+ val draggedTaskChange = state.draggedTaskChange
+ ?: throw IllegalStateException("Expected non-null task change")
+ val sc = draggedTaskChange.leash
+ // TODO(b/301106941): Don't end the animation and start one to scale it back, merge them
+ // instead.
+ // End the animation that shrinks the window when task is first dragged from fullscreen
+ dragToDesktopAnimator.endAnimator()
+ // Then animate the scaled window back to its original bounds.
+ val x: Float = dragToDesktopAnimator.position.x
+ val y: Float = dragToDesktopAnimator.position.y
+ val targetX = draggedTaskChange.endAbsBounds.left
+ val targetY = draggedTaskChange.endAbsBounds.top
+ val dx = targetX - x
+ val dy = targetY - y
+ val tx: SurfaceControl.Transaction = transactionSupplier.get()
+ ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val scale = animator.animatedValue as Float
+ val fraction = animator.animatedFraction
+ val animX = x + (dx * fraction)
+ val animY = y + (dy * fraction)
+ tx.apply {
+ setPosition(sc, animX, animY)
+ setScale(sc, scale, scale)
+ show(sc)
+ apply()
+ }
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ dragToDesktopStateListener?.onCancelToDesktopAnimationEnd(tx)
+ // Start the cancel transition to restore order.
+ startCancelDragToDesktopTransition()
+ }
+ })
+ start()
+ }
+ }
+
+ private fun startCancelDragToDesktopTransition() {
+ val state = requireTransitionState()
+ val wct = WindowContainerTransaction()
+ when (state) {
+ is TransitionState.FromFullscreen -> {
+ val wc = state.draggedTaskChange?.container
+ ?: error("Dragged task should be non-null before cancelling")
+ wct.reorder(wc, true /* toTop */)
+ }
+ is TransitionState.FromSplit -> {
+ val wc = state.splitRootChange?.container
+ ?: error("Split root should be non-null before cancelling")
+ wct.reorder(wc, true /* toTop */)
+ }
+ }
+ val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
+ wct.restoreTransientOrder(homeWc)
+
+ state.cancelTransitionToken = transitions.startTransition(
+ TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
+ }
+
+ private fun clearState() {
+ transitionState = null
+ }
+
+ private fun isSplitTask(taskId: Int): Boolean {
+ return splitScreenController?.isTaskInSplitScreen(taskId) ?: false
+ }
+
+ private fun requireTransitionState(): TransitionState {
+ return transitionState ?: error("Expected non-null transition state")
+ }
+
+ interface DragToDesktopStateListener {
+ fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction)
+ fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction)
+ }
+
+ sealed class TransitionState {
+ abstract val draggedTaskId: Int
+ abstract val dragAnimator: MoveToDesktopAnimator
+ abstract val windowDecoration: DesktopModeWindowDecoration
+ abstract val startTransitionToken: IBinder
+ abstract var startTransitionFinishCb: Transitions.TransitionFinishCallback?
+ abstract var startTransitionFinishTransaction: SurfaceControl.Transaction?
+ abstract var cancelTransitionToken: IBinder?
+ abstract var homeToken: WindowContainerToken?
+ abstract var draggedTaskChange: Change?
+ abstract var cancelled: Boolean
+
+ data class FromFullscreen(
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val windowDecoration: DesktopModeWindowDecoration,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ ) : TransitionState()
+ data class FromSplit(
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val windowDecoration: DesktopModeWindowDecoration,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ var splitRootChange: Change? = null,
+ ) : TransitionState()
+ }
+
+ companion object {
+ /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
+ private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+ }
+}
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 024465b281b8..605600f54823 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
@@ -18,12 +18,13 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_MOVE_TO_DESKTOP;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
@@ -38,11 +39,9 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -60,8 +59,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
public static final int FREEFORM_ANIMATION_DURATION = 336;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
- private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
- private MoveToDesktopAnimator mMoveToDesktopAnimator;
private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
public EnterDesktopTaskTransitionHandler(
@@ -77,61 +74,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
}
/**
- * Starts Transition of a given type
- * @param type Transition type
- * @param wct WindowContainerTransaction for transition
- * @param onAnimationEndCallback to be called after animation
- */
- private void startTransition(@WindowManager.TransitionType int type,
- @NonNull WindowContainerTransaction wct,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mOnAnimationFinishedCallback = onAnimationEndCallback;
- final IBinder token = mTransitions.startTransition(type, wct, this);
- mPendingTransitionTokens.add(token);
- }
-
- /**
- * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
- * to desktop animation
- * @param onAnimationEndCallback to be called after animation
- */
- public void startMoveToDesktop(@NonNull WindowContainerTransaction wct,
- @NonNull MoveToDesktopAnimator moveToDesktopAnimator,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
- * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param onAnimationEndCallback to be called after animation
- */
- public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
- * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
- * to desktop animation
- * @param onAnimationEndCallback to be called after animation
- */
- public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct,
- MoveToDesktopAnimator moveToDesktopAnimator,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
* Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
* @param wct WindowContainerTransaction for transition
* @param decor {@link DesktopModeWindowDecoration} of task being animated
@@ -139,8 +81,8 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
public void moveToDesktop(@NonNull WindowContainerTransaction wct,
DesktopModeWindowDecoration decor) {
mDesktopModeWindowDecoration = decor;
- startTransition(Transitions.TRANSIT_MOVE_TO_DESKTOP, wct,
- null /* onAnimationEndCallback */);
+ final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this);
+ mPendingTransitionTokens.add(token);
}
@Override
@@ -182,30 +124,11 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (type == Transitions.TRANSIT_MOVE_TO_DESKTOP
+ if (type == TRANSIT_MOVE_TO_DESKTOP
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return animateMoveToDesktop(change, startT, finishCallback);
}
- if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return animateStartDragToDesktopMode(change, startT, finishT, finishCallback);
- }
-
- final Rect endBounds = change.getEndAbsBounds();
- if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && !endBounds.isEmpty()) {
- return animateFinalizeDragToDesktopMode(change, startT, finishT, finishCallback,
- endBounds);
- }
-
- if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return animateCancelDragToDesktopMode(change, startT, finishT, finishCallback,
- endBounds);
- }
-
return false;
}
@@ -248,142 +171,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
return true;
}
- private boolean animateStartDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
- // to null and we don't require an animation
- final SurfaceControl sc = change.getLeash();
- startT.setWindowCrop(sc, null);
-
- if (mMoveToDesktopAnimator == null
- || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
- Slog.e(TAG, "No animator available for this transition");
- return false;
- }
-
- // Calculate and set position of the task
- final PointF position = mMoveToDesktopAnimator.getPosition();
- startT.setPosition(sc, position.x, position.y);
- finishT.setPosition(sc, position.x, position.y);
-
- startT.apply();
-
- mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null));
-
- return true;
- }
-
- private boolean animateFinalizeDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Rect endBounds) {
- // This Transition animates a task to freeform bounds after being dragged into freeform
- // mode and brings the remaining freeform tasks to front
- final SurfaceControl sc = change.getLeash();
- startT.setWindowCrop(sc, endBounds.width(),
- endBounds.height());
- startT.apply();
-
- // End the animation that shrinks the window when task is first dragged from fullscreen
- if (mMoveToDesktopAnimator != null) {
- mMoveToDesktopAnimator.endAnimator();
- }
-
- // We want to find the scale of the current bounds relative to the end bounds. The
- // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
- // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
- // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
- final ValueAnimator animator =
- ValueAnimator.ofFloat(
- MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
- animator.setDuration(FREEFORM_ANIMATION_DURATION);
- final SurfaceControl.Transaction t = mTransactionSupplier.get();
- animator.addUpdateListener(animation -> {
- final float animationValue = (float) animation.getAnimatedValue();
- t.setScale(sc, animationValue, animationValue);
-
- final float animationWidth = endBounds.width() * animationValue;
- final float animationHeight = endBounds.height() * animationValue;
- final int animationX = endBounds.centerX() - (int) (animationWidth / 2);
- final int animationY = endBounds.centerY() - (int) (animationHeight / 2);
-
- t.setPosition(sc, animationX, animationY);
- t.apply();
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mOnAnimationFinishedCallback != null) {
- mOnAnimationFinishedCallback.accept(finishT);
- }
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null));
- }
- });
-
- animator.start();
- return true;
- }
- private boolean animateCancelDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Rect endBounds) {
- // This Transition animates a task to fullscreen after being dragged from the status
- // bar and then released back into the status bar area
- final SurfaceControl sc = change.getLeash();
- // Hide the first (fullscreen) frame because the animation will start from the smaller
- // scale size.
- startT.hide(sc)
- .setWindowCrop(sc, endBounds.width(), endBounds.height())
- .apply();
-
- if (mMoveToDesktopAnimator == null
- || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
- Slog.e(TAG, "No animator available for this transition");
- return false;
- }
-
- // End the animation that shrinks the window when task is first dragged from fullscreen
- mMoveToDesktopAnimator.endAnimator();
-
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
- animator.setDuration(FREEFORM_ANIMATION_DURATION);
- final SurfaceControl.Transaction t = mTransactionSupplier.get();
-
- // Get position of the task
- final float x = mMoveToDesktopAnimator.getPosition().x;
- final float y = mMoveToDesktopAnimator.getPosition().y;
-
- animator.addUpdateListener(animation -> {
- final float scale = (float) animation.getAnimatedValue();
- t.setPosition(sc, x * (1 - scale), y * (1 - scale))
- .setScale(sc, scale, scale)
- .show(sc)
- .apply();
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mOnAnimationFinishedCallback != null) {
- mOnAnimationFinishedCallback.accept(finishT);
- }
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null));
- }
- });
- animator.start();
- return true;
- }
-
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index cbed4b5a501f..a58d94ecd19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -81,15 +81,35 @@ public class PipSurfaceTransactionHelper {
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds) {
+ mTmpDestinationRectF.set(destinationBounds);
+ return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */);
+ }
+
+ /**
+ * Operates the scale (setMatrix) on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, RectF destinationBounds) {
return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
}
/**
- * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
+ * Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds, float degrees) {
+ mTmpDestinationRectF.set(destinationBounds);
+ return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees);
+ }
+
+ /**
+ * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, RectF destinationBounds, float degrees) {
mTmpSourceRectF.set(sourceBounds);
// We want the matrix to position the surface relative to the screen coordinates so offset
// the source to 0,0
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 083cd08e9a17..b4067d0db112 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
@@ -63,6 +63,7 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
@@ -120,6 +121,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
+ private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS =
+ SystemProperties.getInt(
+ "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 0);
+
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -292,9 +297,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// changed RunningTaskInfo when it finishes.
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
- private SurfaceControl mLeash;
+ protected SurfaceControl mLeash;
protected PipTransitionState mPipTransitionState;
- private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ protected PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
protected PictureInPictureParams mPictureInPictureParams;
private IntConsumer mOnDisplayIdChangeCallback;
@@ -968,7 +973,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- cancelCurrentAnimator();
+ cancelAnimationOnTaskVanished();
onExitPipFinished(info);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -976,6 +981,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
}
+ protected void cancelAnimationOnTaskVanished() {
+ cancelCurrentAnimator();
+ }
+
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
@@ -1095,7 +1104,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/** Called when exiting PIP transition is finished to do the state cleanup. */
- void onExitPipFinished(TaskInfo info) {
+ public void onExitPipFinished(TaskInfo info) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"onExitPipFinished: %s, state=%s leash=%s",
info.topActivity, mPipTransitionState, mLeash);
@@ -1863,7 +1872,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
removeContentOverlay(surface, callback);
}
});
- animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0);
+ animator.setStartDelay(withStartDelay
+ ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS
+ : EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
animator.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index d5b29e384c09..d5fab441cd46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -822,14 +822,23 @@ public class PipTransition extends PipTransitionController {
+ "participant");
}
- // Make sure other open changes are visible as entering PIP. Some may be hidden in
- // Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
+ // Make sure other non-pip changes are handled correctly.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change == enterPip) continue;
if (TransitionUtil.isOpeningType(change.getMode())) {
+ // For other open changes that are visible when entering PIP, some may be hidden in
+ // Transitions#setupStartState because the transition type is OPEN (such as
+ // auto-enter).
final SurfaceControl leash = change.getLeash();
startTransaction.show(leash).setAlpha(leash, 1.f);
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
+ // For other close changes that are invisible as entering PIP, hide them immediately
+ // to avoid showing a freezing surface.
+ // Ideally, we should let other handler to handle them (likely RemoteHandler by
+ // Launcher).
+ final SurfaceControl leash = change.getLeash();
+ startTransaction.hide(leash);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index fc34772f2fce..63cef9e41f98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -107,7 +107,7 @@ public class PipMenuView extends FrameLayout {
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
- private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+ private static final float MENU_BACKGROUND_ALPHA = 0.54f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
private int mMenuState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index a48e969fde35..72c0cd71f198 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import java.util.Collections;
import java.util.Set;
/**
@@ -101,12 +102,29 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
&& mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0
&& !mTvPipBoundsState.isTvPipManuallyCollapsed();
if (isPipExpanded) {
- updateGravityOnExpansionToggled(/* expanding= */ true);
+ updateGravityOnExpansionToggled(/* expanding= */ isPipExpanded);
}
mTvPipBoundsState.setTvPipExpanded(isPipExpanded);
return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
}
+ @Override
+ public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getEntryDestinationBoundsIgnoringKeepClearAreas()", TAG);
+
+ updateExpandedPipSize();
+ final boolean isPipExpanded = mTvPipBoundsState.isTvExpandedPipSupported()
+ && mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0
+ && !mTvPipBoundsState.isTvPipManuallyCollapsed();
+ if (isPipExpanded) {
+ updateGravityOnExpansionToggled(/* expanding= */ isPipExpanded);
+ }
+ mTvPipBoundsState.setTvPipExpanded(isPipExpanded);
+ return adjustBoundsForTemporaryDecor(getTvPipPlacement(Collections.emptySet(),
+ Collections.emptySet()).getUnstashedBounds());
+ }
+
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
@Override
public Rect getAdjustedDestinationBounds(Rect currentBounds, float newAspectRatio) {
@@ -133,16 +151,25 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
*/
@NonNull
public Placement getTvPipPlacement() {
+ final Set<Rect> restrictedKeepClearAreas = mTvPipBoundsState.getRestrictedKeepClearAreas();
+ final Set<Rect> unrestrictedKeepClearAreas =
+ mTvPipBoundsState.getUnrestrictedKeepClearAreas();
+
+ return getTvPipPlacement(restrictedKeepClearAreas, unrestrictedKeepClearAreas);
+ }
+
+ /**
+ * Calculates the PiP bounds.
+ */
+ @NonNull
+ private Placement getTvPipPlacement(Set<Rect> restrictedKeepClearAreas,
+ Set<Rect> unrestrictedKeepClearAreas) {
final Size pipSize = getPipSize();
final Rect displayBounds = mTvPipBoundsState.getDisplayBounds();
final Size screenSize = new Size(displayBounds.width(), displayBounds.height());
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- final Set<Rect> restrictedKeepClearAreas = mTvPipBoundsState.getRestrictedKeepClearAreas();
- final Set<Rect> unrestrictedKeepClearAreas =
- mTvPipBoundsState.getUnrestrictedKeepClearAreas();
-
mKeepClearAlgorithm.setGravity(mTvPipBoundsState.getTvPipGravity());
mKeepClearAlgorithm.setScreenSize(screenSize);
mKeepClearAlgorithm.setMovementBounds(insetBounds);
@@ -189,8 +216,11 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
int updatedGravity;
if (expanding) {
- // Save collapsed gravity.
- mTvPipBoundsState.setTvPipPreviousCollapsedGravity(mTvPipBoundsState.getTvPipGravity());
+ if (!mTvPipBoundsState.isTvPipExpanded()) {
+ // Save collapsed gravity.
+ mTvPipBoundsState.setTvPipPreviousCollapsedGravity(
+ mTvPipBoundsState.getTvPipGravity());
+ }
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
updatedGravity = Gravity.CENTER_HORIZONTAL | currentY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index 2b3a93e3c3e8..5ee3734e371d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -131,6 +131,7 @@ public class TvPipBoundsState extends PipBoundsState {
mTvFixedPipOrientation = ORIENTATION_UNDETERMINED;
mTvPipGravity = mDefaultGravity;
mPreviousCollapsedGravity = mDefaultGravity;
+ mIsTvPipExpanded = false;
mTvPipManuallyCollapsed = false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 72115fdefa05..cd3d38b6500c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -56,6 +56,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellController;
@@ -122,6 +123,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final PipDisplayLayoutState mPipDisplayLayoutState;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final TvPipBoundsController mTvPipBoundsController;
+ private final PipTransitionState mPipTransitionState;
private final PipAppOpsListener mAppOpsListener;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
@@ -157,6 +159,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
+ PipTransitionState pipTransitionState,
PipAppOpsListener pipAppOpsListener,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
@@ -177,6 +180,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
pipDisplayLayoutState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
+ pipTransitionState,
pipAppOpsListener,
pipTaskOrganizer,
pipTransitionController,
@@ -199,6 +203,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
+ PipTransitionState pipTransitionState,
PipAppOpsListener pipAppOpsListener,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
@@ -212,6 +217,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
Handler mainHandler,
ShellExecutor mainExecutor) {
mContext = context;
+ mPipTransitionState = pipTransitionState;
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
mShellController = shellController;
@@ -365,7 +371,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
"%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
- onPipDisappeared();
}
private void togglePipExpansion() {
@@ -420,6 +425,11 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void onPipTargetBoundsChange(Rect targetBounds, int animationDuration) {
+ if (!mPipTransitionState.hasEnteredPip()) {
+ // Do not schedule a move animation while we're still transitioning into/out of PiP
+ return;
+ }
+
mPipTaskOrganizer.scheduleAnimateResizePip(targetBounds,
animationDuration, null);
mTvPipMenuController.onPipTransitionToTargetBoundsStarted(targetBounds);
@@ -447,7 +457,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return;
}
mPipTaskOrganizer.removePip();
- onPipDisappeared();
+ mTvPipMenuController.closeMenu();
}
@Override
@@ -477,7 +487,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipNotificationController.dismiss();
mActionBroadcastReceiver.unregister();
- mTvPipMenuController.closeMenu();
+ mTvPipMenuController.detach();
mTvPipActionsProvider.reset();
mTvPipBoundsState.resetTvPipState();
mTvPipBoundsController.reset();
@@ -501,8 +511,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void onPipTransitionCanceled(int direction) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onPipTransition_Canceled(), state=%s", TAG, stateToName(mState));
- mTvPipMenuController.onPipTransitionFinished(
- PipAnimationController.isInPipDirection(direction));
mTvPipActionsProvider.updatePipExpansionState(mTvPipBoundsState.isTvPipExpanded());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index ee55211a73a9..c6803f7beebd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -262,8 +262,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void detach() {
- closeMenu();
detachPipMenu();
+ switchToMenuMode(MODE_NO_MENU);
mLeash = null;
}
@@ -320,10 +320,21 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction pipTx,
Rect pipBounds, float alpha) {
+ movePipMenu(pipTx, pipBounds, alpha);
+ }
+
+ /**
+ * Move the PiP menu with the given bounds and update its opacity.
+ * The PiP SurfaceControl is given if there is a need to synchronize the movements
+ * on the same frame as PiP.
+ */
+ public void movePipMenu(@Nullable SurfaceControl.Transaction pipTx, @Nullable Rect pipBounds,
+ float alpha) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipMenu: %s, alpha %s", TAG, pipBounds.toShortString(), alpha);
+ "%s: movePipMenu: %s, alpha %s", TAG,
+ pipBounds != null ? pipBounds.toShortString() : null, alpha);
- if (pipBounds.isEmpty()) {
+ if ((pipBounds == null || pipBounds.isEmpty()) && alpha == ALPHA_NO_CHANGE) {
if (pipTx == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: no transaction given", TAG);
@@ -334,28 +345,36 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
return;
}
- final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView);
- final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView);
- final Rect menuDestBounds = calculateMenuSurfaceBounds(pipBounds);
if (pipTx == null) {
pipTx = new SurfaceControl.Transaction();
}
- pipTx.setPosition(frontSurface, menuDestBounds.left, menuDestBounds.top);
- pipTx.setPosition(backSurface, menuDestBounds.left, menuDestBounds.top);
+
+ final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView);
+ final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView);
+
+ if (pipBounds != null) {
+ final Rect menuDestBounds = calculateMenuSurfaceBounds(pipBounds);
+ pipTx.setPosition(frontSurface, menuDestBounds.left, menuDestBounds.top);
+ pipTx.setPosition(backSurface, menuDestBounds.left, menuDestBounds.top);
+ updateMenuBounds(pipBounds);
+ }
if (alpha != ALPHA_NO_CHANGE) {
pipTx.setAlpha(frontSurface, alpha);
pipTx.setAlpha(backSurface, alpha);
}
- // Synchronize drawing the content in the front and back surfaces together with the pip
- // transaction and the position change for the front and back surfaces
- final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup("TvPip");
- syncGroup.add(mPipMenuView.getRootSurfaceControl(), null);
- syncGroup.add(mPipBackgroundView.getRootSurfaceControl(), null);
- updateMenuBounds(pipBounds);
- syncGroup.addTransaction(pipTx);
- syncGroup.markSyncReady();
+ if (pipBounds != null) {
+ // Synchronize drawing the content in the front and back surfaces together with the pip
+ // transaction and the position change for the front and back surfaces
+ final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup("TvPip");
+ syncGroup.add(mPipMenuView.getRootSurfaceControl(), null);
+ syncGroup.add(mPipBackgroundView.getRootSurfaceControl(), null);
+ syncGroup.addTransaction(pipTx);
+ syncGroup.markSyncReady();
+ } else {
+ pipTx.apply();
+ }
}
private boolean isMenuAttached() {
@@ -388,14 +407,19 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
final Rect menuBounds = calculateMenuSurfaceBounds(pipBounds);
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: updateMenuBounds: %s", TAG, menuBounds.toShortString());
- mSystemWindows.updateViewLayout(mPipBackgroundView,
- getPipMenuLayoutParams(mContext, BACKGROUND_WINDOW_TITLE, menuBounds.width(),
- menuBounds.height()));
- mSystemWindows.updateViewLayout(mPipMenuView,
- getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, menuBounds.width(),
- menuBounds.height()));
- if (mPipMenuView != null) {
- mPipMenuView.setPipBounds(pipBounds);
+
+ boolean needsRelayout = mPipBackgroundView.getLayoutParams().width != menuBounds.width()
+ || mPipBackgroundView.getLayoutParams().height != menuBounds.height();
+ if (needsRelayout) {
+ mSystemWindows.updateViewLayout(mPipBackgroundView,
+ getPipMenuLayoutParams(mContext, BACKGROUND_WINDOW_TITLE, menuBounds.width(),
+ menuBounds.height()));
+ mSystemWindows.updateViewLayout(mPipMenuView,
+ getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, menuBounds.width(),
+ menuBounds.height()));
+ if (mPipMenuView != null) {
+ mPipMenuView.setPipBounds(pipBounds);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
index f86f987039ba..202d36f0dfbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
@@ -168,6 +168,9 @@ class TvPipMenuEduTextDrawer extends FrameLayout {
* that the edu text will be marqueed
*/
private boolean isEduTextMarqueed() {
+ if (mEduTextView.getLayout() == null) {
+ return false;
+ }
final int availableWidth = (int) mEduTextView.getWidth()
- mEduTextView.getCompoundPaddingLeft()
- mEduTextView.getCompoundPaddingRight();
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 f315afba9a03..21223c9ac362 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
@@ -35,7 +35,6 @@ import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -46,6 +45,7 @@ import java.util.Optional;
* TV specific changes to the PipTaskOrganizer.
*/
public class TvPipTaskOrganizer extends PipTaskOrganizer {
+ private final TvPipTransition mTvPipTransition;
public TvPipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@@ -56,7 +56,7 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
@NonNull PipMenuController pipMenuController,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
- @NonNull PipTransitionController pipTransitionController,
+ @NonNull TvPipTransition tvPipTransition,
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@@ -65,9 +65,10 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
ShellExecutor mainExecutor) {
super(context, syncTransactionQueue, pipTransitionState, pipBoundsState,
pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController,
- surfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+ surfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer,
mainExecutor);
+ mTvPipTransition = tvPipTransition;
}
@Override
@@ -105,4 +106,14 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
// when the menu alpha is 0 (e.g. when a fade-in animation starts).
return true;
}
+
+ @Override
+ protected void cancelAnimationOnTaskVanished() {
+ mTvPipTransition.cancelAnimations();
+ if (mLeash != null) {
+ mSurfaceControlTransactionFactory.getTransaction()
+ .setAlpha(mLeash, 0f)
+ .apply();
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index f24b2b385cad..571c839adf11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -16,43 +16,822 @@
package com.android.wm.shell.pip.tv;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.transitTypeToString;
+
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipMenuController.ALPHA_NO_CHANGE;
+import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP;
+import static com.android.wm.shell.pip.PipTransitionState.ENTERING_PIP;
+import static com.android.wm.shell.pip.PipTransitionState.EXITING_PIP;
+import static com.android.wm.shell.pip.PipTransitionState.UNDEFINED;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+
+import android.animation.AnimationHandler;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.os.Trace;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipTransition;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
-import java.util.Optional;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
/**
* PiP Transition for TV.
*/
-public class TvPipTransition extends PipTransition {
+public class TvPipTransition extends PipTransitionController {
+ private static final String TAG = "TvPipTransition";
+ private static final float ZOOM_ANIMATION_SCALE_FACTOR = 0.97f;
+
+ private final PipTransitionState mPipTransitionState;
+ private final PipAnimationController mPipAnimationController;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final TvPipMenuController mTvPipMenuController;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
+ private final PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory
+ mTransactionFactory;
+
+ private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ ThreadLocal.withInitial(() -> {
+ AnimationHandler handler = new AnimationHandler();
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ return handler;
+ });
+
+ private final long mEnterFadeOutDuration;
+ private final long mEnterFadeInDuration;
+ private final long mExitFadeOutDuration;
+ private final long mExitFadeInDuration;
+
+ @Nullable
+ private Animator mCurrentAnimator;
+
+ /**
+ * The Task window that is currently in PIP windowing mode.
+ */
+ @Nullable
+ private WindowContainerToken mCurrentPipTaskToken;
+
+ @Nullable
+ private IBinder mPendingExitTransition;
public TvPipTransition(Context context,
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
TvPipBoundsState tvPipBoundsState,
- PipDisplayLayoutState pipDisplayLayoutState,
- PipTransitionState pipTransitionState,
TvPipMenuController tvPipMenuController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
+ PipTransitionState pipTransitionState,
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<SplitScreenController> splitScreenOptional) {
- super(context, shellInit, shellTaskOrganizer, transitions, tvPipBoundsState,
- pipDisplayLayoutState, pipTransitionState, tvPipMenuController,
- tvPipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
- splitScreenOptional);
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ super(shellInit, shellTaskOrganizer, transitions, tvPipBoundsState, tvPipMenuController,
+ tvPipBoundsAlgorithm);
+ mPipTransitionState = pipTransitionState;
+ mPipAnimationController = pipAnimationController;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+ mTvPipMenuController = tvPipMenuController;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+ mTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+
+ mEnterFadeOutDuration = context.getResources().getInteger(
+ R.integer.config_tvPipEnterFadeOutDuration);
+ mEnterFadeInDuration = context.getResources().getInteger(
+ R.integer.config_tvPipEnterFadeInDuration);
+ mExitFadeOutDuration = context.getResources().getInteger(
+ R.integer.config_tvPipExitFadeOutDuration);
+ mExitFadeInDuration = context.getResources().getInteger(
+ R.integer.config_tvPipExitFadeInDuration);
+ }
+
+ @Override
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
+ cancelAnimations();
+ mPendingExitTransition = mTransitions.startTransition(type, out, this);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (isCloseTransition(info)) {
+ // PiP is closing (without reentering fullscreen activity)
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Starting close animation", TAG);
+ cancelAnimations();
+ startCloseAnimation(info, startTransaction, finishTransaction, finishCallback);
+ mCurrentPipTaskToken = null;
+ return true;
+
+ } else if (transition.equals(mPendingExitTransition)) {
+ // PiP is exiting (reentering fullscreen activity)
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Starting exit animation", TAG);
+
+ final TransitionInfo.Change currentPipTaskChange = findCurrentPipTaskChange(info);
+ mPendingExitTransition = null;
+ // PipTaskChange can be null if the PIP task has been detached, for example, when the
+ // task contains multiple activities, the PIP will be moved to a new PIP task when
+ // entering, and be moved back when exiting. In that case, the PIP task will be removed
+ // immediately.
+ final TaskInfo pipTaskInfo = currentPipTaskChange != null
+ ? currentPipTaskChange.getTaskInfo()
+ : mPipOrganizer.getTaskInfo();
+ if (pipTaskInfo == null) {
+ throw new RuntimeException("Cannot find the pip task for exit-pip transition.");
+ }
+
+ final int type = info.getType();
+ switch (type) {
+ case TRANSIT_EXIT_PIP -> {
+ TransitionInfo.Change pipChange = currentPipTaskChange;
+ SurfaceControl activitySc = null;
+ if (mCurrentPipTaskToken == null) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: There is no existing PiP Task for TRANSIT_EXIT_PIP", TAG);
+ } else if (pipChange == null) {
+ // The pipTaskChange is null, this can happen if we are reparenting the
+ // PIP activity back to its original Task. In that case, we should animate
+ // the activity leash instead, which should be the change whose last parent
+ // is the recorded PiP Task.
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (mCurrentPipTaskToken.equals(change.getLastParent())) {
+ // Find the activity that is exiting PiP.
+ pipChange = change;
+ activitySc = change.getLeash();
+ break;
+ }
+ }
+ }
+ if (pipChange == null) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: No window of exiting PIP is found. Can't play expand "
+ + "animation",
+ TAG);
+ removePipImmediately(info, pipTaskInfo, startTransaction, finishTransaction,
+ finishCallback);
+ return true;
+ }
+ final TransitionInfo.Root root = TransitionUtil.getRootFor(pipChange, info);
+ final SurfaceControl pipLeash;
+ if (activitySc != null) {
+ // Use a local leash to animate activity in case the activity has
+ // letterbox which may be broken by PiP animation, e.g. always end at 0,0
+ // in parent and unable to include letterbox area in crop bounds.
+ final SurfaceControl activitySurface = pipChange.getLeash();
+ pipLeash = new SurfaceControl.Builder()
+ .setName(activitySc + "_pip-leash")
+ .setContainerLayer()
+ .setHidden(false)
+ .setParent(root.getLeash())
+ .build();
+ startTransaction.reparent(activitySurface, pipLeash);
+ // Put the activity at local position with offset in case it is letterboxed.
+ final Point activityOffset = pipChange.getEndRelOffset();
+ startTransaction.setPosition(activitySc, activityOffset.x,
+ activityOffset.y);
+ } else {
+ pipLeash = pipChange.getLeash();
+ startTransaction.reparent(pipLeash, root.getLeash());
+ }
+ startTransaction.setLayer(pipLeash, Integer.MAX_VALUE);
+ final Rect currentBounds = mPipBoundsState.getBounds();
+ final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
+ cancelAnimations();
+ startExitAnimation(pipTaskInfo, pipLeash, currentBounds, destinationBounds,
+ startTransaction,
+ finishTransaction, finishCallback);
+ }
+ // pass through here is intended
+ case TRANSIT_TO_BACK, TRANSIT_REMOVE_PIP -> removePipImmediately(info, pipTaskInfo,
+ startTransaction, finishTransaction,
+ finishCallback
+ );
+ default -> {
+ return false;
+ }
+ }
+ mCurrentPipTaskToken = null;
+ return true;
+
+ } else if (isEnteringPip(info)) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Starting enter animation", TAG);
+
+ // Search for an Enter PiP transition
+ TransitionInfo.Change enterPip = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ enterPip = change;
+ }
+ }
+ if (enterPip == null) {
+ throw new IllegalStateException("Trying to start PiP animation without a pip"
+ + "participant");
+ }
+
+ // Make sure other open changes are visible as entering PIP. Some may be hidden in
+ // Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == enterPip) continue;
+ if (TransitionUtil.isOpeningType(change.getMode())) {
+ final SurfaceControl leash = change.getLeash();
+ startTransaction.show(leash).setAlpha(leash, 1.f);
+ }
+ }
+
+ cancelAnimations();
+ startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * For {@link Transitions#TRANSIT_REMOVE_PIP}, we just immediately remove the PIP Task.
+ */
+ private void removePipImmediately(@NonNull TransitionInfo info,
+ @NonNull TaskInfo taskInfo, @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: removePipImmediately", TAG);
+ cancelAnimations();
+ startTransaction.apply();
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipDisplayLayoutState.getDisplayBounds());
+ mTvPipMenuController.detach();
+ mPipOrganizer.onExitPipFinished(taskInfo);
+ finishCallback.onTransitionFinished(/* wct= */ null);
+
+ mPipTransitionState.setTransitionState(UNDEFINED);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_REMOVE_STACK);
+ }
+
+ private void startCloseAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final TransitionInfo.Change pipTaskChange = findCurrentPipTaskChange(info);
+ final SurfaceControl pipLeash = pipTaskChange.getLeash();
+
+ final List<SurfaceControl> closeLeashes = new ArrayList<>();
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (TransitionUtil.isClosingType(change.getMode()) && change != pipTaskChange) {
+ closeLeashes.add(change.getLeash());
+ }
+ }
+
+ final Rect pipBounds = mPipBoundsState.getBounds();
+ mSurfaceTransactionHelper
+ .resetScale(startTransaction, pipLeash, pipBounds)
+ .crop(startTransaction, pipLeash, pipBounds)
+ .shadow(startTransaction, pipLeash, false);
+
+ final SurfaceControl.Transaction transaction = mTransactionFactory.getTransaction();
+ for (SurfaceControl leash : closeLeashes) {
+ startTransaction.setShadowRadius(leash, 0f);
+ }
+
+ ValueAnimator closeFadeOutAnimator = createAnimator();
+ closeFadeOutAnimator.setInterpolator(TvPipInterpolators.EXIT);
+ closeFadeOutAnimator.setDuration(mExitFadeOutDuration);
+ closeFadeOutAnimator.addUpdateListener(
+ animationUpdateListener(pipLeash).fadingOut().withMenu());
+ for (SurfaceControl leash : closeLeashes) {
+ closeFadeOutAnimator.addUpdateListener(animationUpdateListener(leash).fadingOut());
+ }
+
+ closeFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: close animation: start", TAG);
+ for (SurfaceControl leash : closeLeashes) {
+ startTransaction.setShadowRadius(leash, 0f);
+ }
+ startTransaction.apply();
+
+ mPipTransitionState.setTransitionState(EXITING_PIP);
+ sendOnPipTransitionStarted(TRANSITION_DIRECTION_REMOVE_STACK);
+ }
+
+ @Override
+ public void onAnimationCancel(@NonNull Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: close animation: cancel", TAG);
+ sendOnPipTransitionCancelled(TRANSITION_DIRECTION_REMOVE_STACK);
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: close animation: end", TAG);
+ mTvPipMenuController.detach();
+ finishCallback.onTransitionFinished(null /* wct */);
+ transaction.close();
+ mPipTransitionState.setTransitionState(UNDEFINED);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_REMOVE_STACK);
+
+ mCurrentAnimator = null;
+ }
+ });
+
+ closeFadeOutAnimator.start();
+ mCurrentAnimator = closeFadeOutAnimator;
+ }
+
+ @Override
+ public void startEnterAnimation(@NonNull TransitionInfo.Change pipChange,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Keep track of the PIP task
+ mCurrentPipTaskToken = pipChange.getContainer();
+ final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo();
+ final SurfaceControl leash = pipChange.getLeash();
+
+ mTvPipMenuController.attach(leash);
+ setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
+ taskInfo.topActivityInfo);
+
+ final Rect pipBounds =
+ mPipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas();
+ mPipBoundsState.setBounds(pipBounds);
+ mTvPipMenuController.movePipMenu(null, pipBounds, 0f);
+
+ final WindowContainerTransaction resizePipWct = new WindowContainerTransaction();
+ resizePipWct.setWindowingMode(taskInfo.token, WINDOWING_MODE_PINNED);
+ resizePipWct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_PINNED);
+ resizePipWct.setBounds(taskInfo.token, pipBounds);
+
+ mSurfaceTransactionHelper
+ .resetScale(finishTransaction, leash, pipBounds)
+ .crop(finishTransaction, leash, pipBounds)
+ .shadow(finishTransaction, leash, false);
+
+ final Rect currentBounds = pipChange.getStartAbsBounds();
+ final Rect fadeOutCurrentBounds = scaledRect(currentBounds, ZOOM_ANIMATION_SCALE_FACTOR);
+
+ final ValueAnimator enterFadeOutAnimator = createAnimator();
+ enterFadeOutAnimator.setInterpolator(TvPipInterpolators.EXIT);
+ enterFadeOutAnimator.setDuration(mEnterFadeOutDuration);
+ enterFadeOutAnimator.addUpdateListener(
+ animationUpdateListener(leash)
+ .fadingOut()
+ .animateBounds(currentBounds, fadeOutCurrentBounds, currentBounds));
+
+ enterFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
+ @SuppressLint("MissingPermission")
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: enter fade out animation: end", TAG);
+ SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .resetScale(tx, leash, pipBounds)
+ .crop(tx, leash, pipBounds)
+ .shadow(tx, leash, false);
+ mShellTaskOrganizer.applyTransaction(resizePipWct);
+ tx.apply();
+ }
+ });
+
+ final ValueAnimator enterFadeInAnimator = createAnimator();
+ enterFadeInAnimator.setInterpolator(TvPipInterpolators.ENTER);
+ enterFadeInAnimator.setDuration(mEnterFadeInDuration);
+ enterFadeInAnimator.addUpdateListener(
+ animationUpdateListener(leash)
+ .fadingIn()
+ .withMenu()
+ .atBounds(pipBounds));
+
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet
+ .play(enterFadeInAnimator)
+ .after(500)
+ .after(enterFadeOutAnimator);
+
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: enter animation: start", TAG);
+ startTransaction.apply();
+ mPipTransitionState.setTransitionState(ENTERING_PIP);
+ sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: enter animation: cancel", TAG);
+ enterFadeInAnimator.setCurrentFraction(1f);
+ sendOnPipTransitionCancelled(TRANSITION_DIRECTION_TO_PIP);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: enter animation: end", TAG);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ wct.setBounds(taskInfo.token, pipBounds);
+ finishCallback.onTransitionFinished(wct);
+
+ mPipTransitionState.setTransitionState(ENTERED_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ mCurrentAnimator = null;
+ }
+ });
+
+ animatorSet.start();
+ mCurrentAnimator = animatorSet;
}
+ private void startExitAnimation(@NonNull TaskInfo taskInfo, SurfaceControl leash,
+ Rect currentBounds, Rect destinationBounds,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final Rect fadeInStartBounds = scaledRect(destinationBounds, ZOOM_ANIMATION_SCALE_FACTOR);
+
+ final ValueAnimator exitFadeOutAnimator = createAnimator();
+ exitFadeOutAnimator.setInterpolator(TvPipInterpolators.EXIT);
+ exitFadeOutAnimator.setDuration(mExitFadeOutDuration);
+ exitFadeOutAnimator.addUpdateListener(
+ animationUpdateListener(leash)
+ .fadingOut()
+ .withMenu()
+ .atBounds(currentBounds));
+ exitFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: exit fade out animation: end", TAG);
+ startTransaction.apply();
+ mPipMenuController.detach();
+ }
+ });
+
+ final ValueAnimator exitFadeInAnimator = createAnimator();
+ exitFadeInAnimator.setInterpolator(TvPipInterpolators.ENTER);
+ exitFadeInAnimator.setDuration(mExitFadeInDuration);
+ exitFadeInAnimator.addUpdateListener(
+ animationUpdateListener(leash)
+ .fadingIn()
+ .animateBounds(fadeInStartBounds, destinationBounds, destinationBounds));
+
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playSequentially(
+ exitFadeOutAnimator,
+ exitFadeInAnimator
+ );
+
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: exit animation: start", TAG);
+ mPipTransitionState.setTransitionState(EXITING_PIP);
+ sendOnPipTransitionStarted(TRANSITION_DIRECTION_LEAVE_PIP);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: exit animation: cancel", TAG);
+ sendOnPipTransitionCancelled(TRANSITION_DIRECTION_LEAVE_PIP);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: exit animation: end", TAG);
+ mPipOrganizer.onExitPipFinished(taskInfo);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ wct.setBounds(taskInfo.token, destinationBounds);
+ finishCallback.onTransitionFinished(wct);
+
+ mPipTransitionState.setTransitionState(UNDEFINED);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_LEAVE_PIP);
+
+ mCurrentAnimator = null;
+ }
+ });
+
+ animatorSet.start();
+ mCurrentAnimator = animatorSet;
+ }
+
+ @NonNull
+ private ValueAnimator createAnimator() {
+ final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
+ return animator;
+ }
+
+ @NonNull
+ private TvPipTransitionAnimatorUpdateListener animationUpdateListener(
+ @NonNull SurfaceControl leash) {
+ return new TvPipTransitionAnimatorUpdateListener(leash, mTvPipMenuController,
+ mTransactionFactory.getTransaction(), mSurfaceTransactionHelper);
+ }
+
+ @NonNull
+ private static Rect scaledRect(@NonNull Rect rect, float scale) {
+ final Rect out = new Rect(rect);
+ out.inset((int) (rect.width() * (1 - scale) / 2), (int) (rect.height() * (1 - scale) / 2));
+ return out;
+ }
+
+ private boolean isCloseTransition(TransitionInfo info) {
+ final TransitionInfo.Change currentPipTaskChange = findCurrentPipTaskChange(info);
+ return currentPipTaskChange != null && info.getType() == TRANSIT_CLOSE;
+ }
+
+ @Nullable
+ private TransitionInfo.Change findCurrentPipTaskChange(@NonNull TransitionInfo info) {
+ if (mCurrentPipTaskToken == null) {
+ return null;
+ }
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (mCurrentPipTaskToken.equals(change.getContainer())) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Whether we should handle the given {@link TransitionInfo} animation as entering PIP.
+ */
+ private boolean isEnteringPip(@NonNull TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (isEnteringPip(change, info.getType())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Whether a particular change is a window that is entering pip.
+ */
+ @Override
+ public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
+ && !Objects.equals(change.getContainer(), mCurrentPipTaskToken)) {
+ if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN
+ || transitType == TRANSIT_CHANGE) {
+ return true;
+ }
+ // Please file a bug to handle the unexpected transition type.
+ android.util.Slog.e(TAG, "Found new PIP in transition with mis-matched type="
+ + transitTypeToString(transitType), new Throwable());
+ }
+ return false;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: merge animation", TAG);
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.end();
+ }
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ if (requestHasPipEnter(request)) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: handle PiP enter request", TAG);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ augmentRequest(transition, request, wct);
+ return wct;
+ } else if (request.getType() == TRANSIT_TO_BACK && request.getTriggerTask() != null
+ && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ // if we receive a TRANSIT_TO_BACK type of request while in PiP
+ mPendingExitTransition = transition;
+
+ // update the transition state to avoid {@link PipTaskOrganizer#onTaskVanished()} calls
+ mPipTransitionState.setTransitionState(EXITING_PIP);
+
+ // return an empty WindowContainerTransaction so that we don't check other handlers
+ return new WindowContainerTransaction();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request,
+ @NonNull WindowContainerTransaction outWCT) {
+ if (!requestHasPipEnter(request)) {
+ throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
+ }
+ outWCT.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED);
+ }
+
+ /**
+ * Cancel any ongoing PiP transitions/animations.
+ */
+ public void cancelAnimations() {
+ if (mPipAnimationController.isAnimating()) {
+ mPipAnimationController.getCurrentAnimator().cancel();
+ mPipAnimationController.resetAnimatorState();
+ }
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.cancel();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.end();
+ }
+ }
+
+ private static class TvPipTransitionAnimatorUpdateListener implements
+ ValueAnimator.AnimatorUpdateListener {
+ private final SurfaceControl mLeash;
+ private final TvPipMenuController mTvPipMenuController;
+ private final SurfaceControl.Transaction mTransaction;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final RectF mTmpRectF = new RectF();
+ private final Rect mTmpRect = new Rect();
+
+ private float mStartAlpha = ALPHA_NO_CHANGE;
+ private float mEndAlpha = ALPHA_NO_CHANGE;
+
+ @Nullable
+ private Rect mStartBounds;
+ @Nullable
+ private Rect mEndBounds;
+ private Rect mWindowContainerBounds;
+ private boolean mShowMenu;
+
+ TvPipTransitionAnimatorUpdateListener(@NonNull SurfaceControl leash,
+ @NonNull TvPipMenuController tvPipMenuController,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ mLeash = leash;
+ mTvPipMenuController = tvPipMenuController;
+ mTransaction = transaction;
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+ }
+
+ public TvPipTransitionAnimatorUpdateListener animateAlpha(
+ @FloatRange(from = 0.0, to = 1.0) float startAlpha,
+ @FloatRange(from = 0.0, to = 1.0) float endAlpha) {
+ mStartAlpha = startAlpha;
+ mEndAlpha = endAlpha;
+ return this;
+ }
+
+ public TvPipTransitionAnimatorUpdateListener animateBounds(@NonNull Rect startBounds,
+ @NonNull Rect endBounds, @NonNull Rect windowContainerBounds) {
+ mStartBounds = startBounds;
+ mEndBounds = endBounds;
+ mWindowContainerBounds = windowContainerBounds;
+ return this;
+ }
+
+ public TvPipTransitionAnimatorUpdateListener atBounds(@NonNull Rect bounds) {
+ return animateBounds(bounds, bounds, bounds);
+ }
+
+ public TvPipTransitionAnimatorUpdateListener fadingOut() {
+ return animateAlpha(1f, 0f);
+ }
+
+ public TvPipTransitionAnimatorUpdateListener fadingIn() {
+ return animateAlpha(0f, 1f);
+ }
+
+ public TvPipTransitionAnimatorUpdateListener withMenu() {
+ mShowMenu = true;
+ return this;
+ }
+
+ @Override
+ public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+ final float fraction = animation.getAnimatedFraction();
+ final float alpha = lerp(mStartAlpha, mEndAlpha, fraction);
+ if (mStartBounds != null && mEndBounds != null) {
+ lerp(mStartBounds, mEndBounds, fraction, mTmpRectF);
+ applyAnimatedValue(alpha, mTmpRectF);
+ } else {
+ applyAnimatedValue(alpha, null);
+ }
+ }
+
+ private void applyAnimatedValue(float alpha, @Nullable RectF bounds) {
+ Trace.beginSection("applyAnimatedValue");
+ final SurfaceControl.Transaction tx = mTransaction;
+
+ Trace.beginSection("leash scale and alpha");
+ if (alpha != ALPHA_NO_CHANGE) {
+ mSurfaceTransactionHelper.alpha(tx, mLeash, alpha);
+ }
+ if (bounds != null) {
+ mSurfaceTransactionHelper.scale(tx, mLeash, mWindowContainerBounds, bounds);
+ }
+ mSurfaceTransactionHelper.shadow(tx, mLeash, false);
+ tx.show(mLeash);
+ Trace.endSection();
+
+ if (mShowMenu) {
+ Trace.beginSection("movePipMenu");
+ if (bounds != null) {
+ mTmpRect.set((int) bounds.left, (int) bounds.top, (int) bounds.right,
+ (int) bounds.bottom);
+ mTvPipMenuController.movePipMenu(tx, mTmpRect, alpha);
+ } else {
+ mTvPipMenuController.movePipMenu(tx, null, alpha);
+ }
+ Trace.endSection();
+ } else {
+ mTvPipMenuController.movePipMenu(tx, null, 0f);
+ }
+
+ tx.apply();
+ Trace.endSection();
+ }
+
+ private float lerp(float start, float end, float fraction) {
+ return start * (1 - fraction) + end * fraction;
+ }
+
+ private void lerp(@NonNull Rect start, @NonNull Rect end, float fraction,
+ @NonNull RectF out) {
+ out.set(
+ start.left * (1 - fraction) + end.left * fraction,
+ start.top * (1 - fraction) + end.top * fraction,
+ start.right * (1 - fraction) + end.right * fraction,
+ start.bottom * (1 - fraction) + end.bottom * fraction);
+ }
+ }
}
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 9bb383f0b61a..0448d94669ce 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
@@ -57,11 +57,6 @@ public class PipScheduler {
@Nullable
private SurfaceControl mPinnedTaskLeash;
- // the leash of the original task of the PiP activity;
- // used to synchronize app drawings in the multi-activity case
- @Nullable
- private SurfaceControl mOriginalTaskLeash;
-
/**
* A temporary broadcast receiver to initiate exit PiP via expand.
* This will later be modified to be triggered by the PiP menu.
@@ -95,10 +90,6 @@ public class PipScheduler {
mPinnedTaskLeash = pinnedTaskLeash;
}
- void setOriginalTaskLeash(SurfaceControl originalTaskLeash) {
- mOriginalTaskLeash = originalTaskLeash;
- }
-
void setPipTaskToken(@Nullable WindowContainerToken pipTaskToken) {
mPipTaskToken = pipTaskToken;
}
@@ -133,6 +124,5 @@ public class PipScheduler {
void onExitPip() {
mPipTaskToken = null;
mPinnedTaskLeash = null;
- mOriginalTaskLeash = 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 7d3bd658d126..6200ea583a48 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.pip2.phone;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -50,7 +49,7 @@ import com.android.wm.shell.transition.Transitions;
public class PipTransition extends PipTransitionController {
private static final String TAG = PipTransition.class.getSimpleName();
- private PipScheduler mPipScheduler;
+ private final PipScheduler mPipScheduler;
@Nullable
private WindowContainerToken mPipTaskToken;
@Nullable
@@ -168,14 +167,9 @@ public class PipTransition extends PipTransitionController {
}
mPipTaskToken = pipChange.getContainer();
- // cache the PiP task token and the relevant leashes
+ // cache the PiP task token and leash
mPipScheduler.setPipTaskToken(mPipTaskToken);
mPipScheduler.setPinnedTaskLeash(pipChange.getLeash());
- // check if we entered PiP from a multi-activity task and set the original task leash
- final int lastParentTaskId = pipChange.getTaskInfo().lastParentTaskIdBeforePip;
- final boolean isSingleActivity = lastParentTaskId == INVALID_TASK_ID;
- mPipScheduler.setOriginalTaskLeash(isSingleActivity ? null :
- findChangeByTaskId(info, lastParentTaskId).getLeash());
startTransaction.apply();
finishCallback.onTransitionFinished(null);
@@ -201,17 +195,6 @@ public class PipTransition extends PipTransitionController {
return null;
}
- @Nullable
- private TransitionInfo.Change findChangeByTaskId(TransitionInfo info, int taskId) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getTaskInfo() != null
- && change.getTaskInfo().taskId == taskId) {
- return change;
- }
- }
- return null;
- }
-
private void onExitPip() {
mPipTaskToken = null;
mPipScheduler.onExitPip();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index c2f15f6cba75..e6418f35a0b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -182,7 +182,7 @@ public class TaskSnapshotWindow {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn);
- mSession.remove(mWindow);
+ mSession.remove(mWindow.asBinder());
} catch (RemoteException e) {
// nothing
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 723a4a7ca664..193a4fb5b503 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -60,6 +60,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import android.animation.Animator;
@@ -424,7 +425,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
+ final int type = getTransitionTypeFromInfo(info);
+ Animation a = loadAnimation(type, info, change, wallpaperTransit, isDreamTransition);
if (a != null) {
if (isTask) {
final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
@@ -660,12 +662,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
@Nullable
- private Animation loadAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, int wallpaperTransit,
- boolean isDreamTransition) {
+ private Animation loadAnimation(@WindowManager.TransitionType int type,
+ @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+ int wallpaperTransit, boolean isDreamTransition) {
Animation a;
- final int type = info.getType();
final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
@@ -721,7 +722,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return null;
} else {
a = loadAttributeAnimation(
- info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
+ type, info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
}
if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index d07d2b7b6db9..b012d359931a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -45,6 +46,7 @@ import android.graphics.Rect;
import android.graphics.Shader;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.window.ScreenCapture;
@@ -61,10 +63,10 @@ public class TransitionAnimationHelper {
/** Loads the animation that is defined through attribute id for the given transition. */
@Nullable
- public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
+ public static Animation loadAttributeAnimation(@WindowManager.TransitionType int type,
+ @NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, int wallpaperTransit,
@NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
- final int type = info.getType();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
final boolean enter = TransitionUtil.isOpeningType(changeMode);
@@ -186,6 +188,38 @@ public class TransitionAnimationHelper {
return options.getCustomActivityTransition(isOpen);
}
+ /**
+ * Gets the final transition type from {@link TransitionInfo} for determining the animation.
+ */
+ public static int getTransitionTypeFromInfo(@NonNull TransitionInfo info) {
+ final int type = info.getType();
+ // If the info transition type is opening transition, iterate its changes to see if it
+ // has any opening change, if none, returns TRANSIT_CLOSE type for closing animation.
+ if (type == TRANSIT_OPEN) {
+ boolean hasOpenTransit = false;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if ((change.getTaskInfo() != null || change.hasFlags(FLAG_IS_DISPLAY))
+ && !TransitionUtil.isOrderOnly(change)) {
+ // This isn't an activity-level transition.
+ return type;
+ }
+ if (change.getTaskInfo() != null
+ && change.hasFlags(FLAG_IS_DISPLAY | FLAGS_IS_NON_APP_WINDOW)) {
+ // Ignore non-activity containers.
+ continue;
+ }
+ if (change.getMode() == TRANSIT_OPEN) {
+ hasOpenTransit = true;
+ break;
+ }
+ }
+ if (!hasOpenTransit) {
+ return TRANSIT_CLOSE;
+ }
+ }
+ return type;
+ }
+
static Animation loadCustomActivityTransition(
@NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
TransitionInfo.AnimationOptions options, boolean enter,
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 ab5c0636f2b5..b98762d5e104 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
@@ -150,19 +150,19 @@ public class Transitions implements RemoteCallable<Transitions>,
/** Transition type for maximize to freeform transition. */
public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
- /** Transition type for starting the move to desktop mode. */
- public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE =
+ /** Transition type for starting the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 10;
- /** Transition type for finalizing the move to desktop mode. */
- public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE =
+ /** Transition type for finalizing the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 11;
/** Transition type to fullscreen from desktop mode. */
public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
- /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
- public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE =
+ /** Transition type to cancel the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
/** Transition type to animate the toggle resize between the max and default desktop sizes. */
@@ -654,12 +654,27 @@ public class Transitions implements RemoteCallable<Transitions>,
info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
info.getDebugId(), transitionToken, info);
- final int activeIdx = findByToken(mPendingTransitions, transitionToken);
+ int activeIdx = findByToken(mPendingTransitions, transitionToken);
if (activeIdx < 0) {
- throw new IllegalStateException("Got transitionReady for non-pending transition "
+ final ActiveTransition existing = getKnownTransition(transitionToken);
+ if (existing != null) {
+ Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
+ // The transition is already somewhere else in the pipeline, so just return here.
+ t.apply();
+ existing.mFinishT.merge(finishT);
+ return;
+ }
+ // This usually means the system is in a bad state and may not recover; however,
+ // there's an incentive to propagate bad states rather than crash, so we're kinda
+ // required to do the same thing I guess.
+ Log.wtf(TAG, "Got transitionReady for non-pending transition "
+ transitionToken + ". expecting one of "
+ Arrays.toString(mPendingTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
+ final ActiveTransition fallback = new ActiveTransition();
+ fallback.mToken = transitionToken;
+ mPendingTransitions.add(fallback);
+ activeIdx = mPendingTransitions.size() - 1;
}
// Move from pending to ready
final ActiveTransition active = mPendingTransitions.remove(activeIdx);
@@ -1050,34 +1065,43 @@ public class Transitions implements RemoteCallable<Transitions>,
processReadyQueue(track);
}
- private boolean isTransitionKnown(IBinder token) {
+ /**
+ * Checks to see if the transition specified by `token` is already known. If so, it will be
+ * returned.
+ */
+ @Nullable
+ private ActiveTransition getKnownTransition(IBinder token) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
- if (mPendingTransitions.get(i).mToken == token) return true;
+ final ActiveTransition active = mPendingTransitions.get(i);
+ if (active.mToken == token) return active;
}
for (int i = 0; i < mReadyDuringSync.size(); ++i) {
- if (mReadyDuringSync.get(i).mToken == token) return true;
+ final ActiveTransition active = mReadyDuringSync.get(i);
+ if (active.mToken == token) return active;
}
for (int t = 0; t < mTracks.size(); ++t) {
final Track tr = mTracks.get(t);
for (int i = 0; i < tr.mReadyTransitions.size(); ++i) {
- if (tr.mReadyTransitions.get(i).mToken == token) return true;
+ final ActiveTransition active = tr.mReadyTransitions.get(i);
+ if (active.mToken == token) return active;
}
final ActiveTransition active = tr.mActiveTransition;
if (active == null) continue;
- if (active.mToken == token) return true;
+ if (active.mToken == token) return active;
if (active.mMerged == null) continue;
for (int m = 0; m < active.mMerged.size(); ++m) {
- if (active.mMerged.get(m).mToken == token) return true;
+ final ActiveTransition merged = active.mMerged.get(m);
+ if (merged.mToken == token) return merged;
}
}
- return false;
+ return null;
}
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
request.getDebugId(), transitionToken, request);
- if (isTransitionKnown(transitionToken)) {
+ if (getKnownTransition(transitionToken) != null) {
throw new RuntimeException("Transition already started " + transitionToken);
}
final ActiveTransition active = new ActiveTransition();
@@ -1161,7 +1185,7 @@ public class Transitions implements RemoteCallable<Transitions>,
*/
private void finishForSync(ActiveTransition reason,
int trackIdx, @Nullable ActiveTransition forceFinish) {
- if (!isTransitionKnown(reason.mToken)) {
+ if (getKnownTransition(reason.mToken) == null) {
Log.d(TAG, "finishForSleep: already played sync transition " + reason);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index b26d0613f3b6..07c54293111c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.unfold;
+import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
@@ -188,23 +189,27 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull TransitionFinishCallback finishCallback) {
- if (info.getType() == TRANSIT_CHANGE) {
- // TODO (b/286928742) unfold transition handler should be part of mixed handler to
- // handle merges better.
- for (int i = 0; i < info.getChanges().size(); ++i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null
- && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
- // Tasks that are always on top (e.g. bubbles), will handle their own transition
- // as they are on top of everything else. So skip merging transitions here.
- return;
- }
+ if (info.getType() != TRANSIT_CHANGE) {
+ return;
+ }
+ if ((info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
+ return;
+ }
+ // TODO (b/286928742) unfold transition handler should be part of mixed handler to
+ // handle merges better.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
+ // Tasks that are always on top (e.g. bubbles), will handle their own transition
+ // as they are on top of everything else. So skip merging transitions here.
+ return;
}
- // Apply changes happening during the unfold animation immediately
- t.apply();
- finishCallback.onTransitionFinished(null);
}
+ // Apply changes happening during the unfold animation immediately
+ t.apply();
+ finishCallback.onTransitionFinished(null);
}
/** Whether `request` contains an unfold action. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index aff35a347183..c12ac8b3772e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -149,7 +149,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mDecorationContainerSurface,
mDragPositioningCallback,
mSurfaceControlBuilderSupplier,
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier,
+ mDisplayController);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
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 e206039aa6bf..dd6ca8da56eb 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
@@ -268,13 +268,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
if (change.getMode() == WindowManager.TRANSIT_CHANGE
- && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
- || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+ && (info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
|| info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) {
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
.addTransitionPausingRelayout(transition);
+ } else if (change.getMode() == WindowManager.TRANSIT_TO_BACK
+ && info.getType() == Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+ && change.getTaskInfo() != null) {
+ final DesktopModeWindowDecoration decor =
+ mWindowDecorByTaskId.get(change.getTaskInfo().taskId);
+ if (decor != null) {
+ decor.addTransitionPausingRelayout(transition);
+ }
}
}
@@ -530,6 +536,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
+ if (e.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ // If a motion event is cancelled, reset mShouldClick so a click is not accidentally
+ // performed.
+ mShouldClick = false;
+ }
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
@@ -765,10 +776,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mMoveToDesktopAnimator = null;
return;
} else if (mMoveToDesktopAnimator != null) {
- relevantDecor.incrementRelayoutBlock();
mDesktopTasksController.ifPresent(
- c -> c.cancelMoveToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator));
+ c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo));
mMoveToDesktopAnimator = null;
return;
}
@@ -790,15 +799,24 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
- closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
- mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface);
+ mContext, mDragToDesktopAnimationStartBounds,
+ relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
mDesktopTasksController.ifPresent(
- c -> c.startMoveToDesktop(relevantDecor.mTaskInfo,
- mDragToDesktopAnimationStartBounds,
- mMoveToDesktopAnimator));
- mMoveToDesktopAnimator.startAnimation();
+ c -> {
+ final int taskId = relevantDecor.mTaskInfo.taskId;
+ relevantDecor.incrementRelayoutBlock();
+ if (isTaskInSplitScreen(taskId)) {
+ final DesktopModeWindowDecoration otherDecor =
+ mWindowDecorByTaskId.get(
+ getOtherSplitTask(taskId).taskId);
+ if (otherDecor != null) {
+ otherDecor.incrementRelayoutBlock();
+ }
+ }
+ c.startDragToDesktop(relevantDecor.mTaskInfo,
+ mMoveToDesktopAnimator, relevantDecor);
+ });
}
}
if (mMoveToDesktopAnimator != null) {
@@ -837,7 +855,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
*/
private void animateToDesktop(DesktopModeWindowDecoration relevantDecor,
MotionEvent ev) {
- relevantDecor.incrementRelayoutBlock();
centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
}
@@ -853,15 +870,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final SurfaceControl sc = relevantDecor.mTaskSurface;
final Rect endBounds = calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE);
final Transaction t = mTransactionFactory.get();
- final float diffX = endBounds.centerX() - ev.getX();
- final float diffY = endBounds.top - ev.getY();
- final float startingX = ev.getX() - DRAG_FREEFORM_SCALE
+ final float diffX = endBounds.centerX() - ev.getRawX();
+ final float diffY = endBounds.top - ev.getRawY();
+ final float startingX = ev.getRawX() - DRAG_FREEFORM_SCALE
* mDragToDesktopAnimationStartBounds.width() / 2;
animator.addUpdateListener(animation -> {
final float animatorValue = (float) animation.getAnimatedValue();
final float x = startingX + diffX * animatorValue;
- final float y = ev.getY() + diffY * animatorValue;
+ final float y = ev.getRawY() + diffY * animatorValue;
t.setPosition(sc, x, y);
t.apply();
});
@@ -869,9 +886,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void onAnimationEnd(Animator animation) {
mDesktopTasksController.ifPresent(
- c -> c.onDragPositioningEndThroughStatusBar(
- relevantDecor.mTaskInfo,
- calculateFreeformBounds(ev.getDisplayId(), FINAL_FREEFORM_SCALE)));
+ c -> {
+ c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(),
+ FINAL_FREEFORM_SCALE));
+ });
}
});
animator.start();
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 eba1a36ef29f..e1d177af2331 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
@@ -293,7 +293,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mDecorationContainerSurface,
mDragPositioningCallback,
mSurfaceControlBuilderSupplier,
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier,
+ mDisplayController);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index 1669cf4a222c..8ce2d6d6d092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -40,8 +40,9 @@ public interface DragPositioningCallback {
* {@code 0} to indicate it's a move
* @param x x coordinate in window decoration coordinate system where the drag starts
* @param y y coordinate in window decoration coordinate system where the drag starts
+ * @return the starting task bounds
*/
- void onDragPositioningStart(@CtrlType int ctrlType, float x, float y);
+ Rect onDragPositioningStart(@CtrlType int ctrlType, float x, float y);
/**
* Called when the pointer moves during a drag-resize or drag-move.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 7c6fb99e9c8b..8cbcde320795 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -50,6 +50,8 @@ import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
import com.android.internal.view.BaseIWindow;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import java.util.function.Supplier;
@@ -78,6 +80,7 @@ class DragResizeInputListener implements AutoCloseable {
private final SurfaceControl mInputSinkSurface;
private final BaseIWindow mFakeSinkWindow;
private final InputChannel mSinkInputChannel;
+ private final DisplayController mDisplayController;
private int mTaskWidth;
private int mTaskHeight;
@@ -92,6 +95,7 @@ class DragResizeInputListener implements AutoCloseable {
private int mDragPointerId = -1;
private DragDetector mDragDetector;
+ private final Region mTouchRegion = new Region();
DragResizeInputListener(
Context context,
@@ -102,7 +106,8 @@ class DragResizeInputListener implements AutoCloseable {
SurfaceControl decorationSurface,
DragPositioningCallback callback,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
- Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ DisplayController displayController) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
@@ -110,6 +115,7 @@ class DragResizeInputListener implements AutoCloseable {
mDisplayId = displayId;
mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
+ mDisplayController = displayController;
// Use a fake window as the backing surface is a container layer, and we don't want to
// create a buffer layer for it, so we can't use ViewRootImpl.
mFakeWindow = new BaseIWindow();
@@ -120,7 +126,7 @@ class DragResizeInputListener implements AutoCloseable {
mWindowSession.grantInputChannel(
mDisplayId,
mDecorationSurface,
- mFakeWindow,
+ mFakeWindow.asBinder(),
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
@@ -155,7 +161,7 @@ class DragResizeInputListener implements AutoCloseable {
mWindowSession.grantInputChannel(
mDisplayId,
mInputSinkSurface,
- mFakeSinkWindow,
+ mFakeSinkWindow.asBinder(),
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
0 /* privateFlags */,
@@ -195,34 +201,34 @@ class DragResizeInputListener implements AutoCloseable {
mCornerSize = cornerSize;
mDragDetector.setTouchSlop(touchSlop);
- Region touchRegion = new Region();
+ mTouchRegion.setEmpty();
final Rect topInputBounds = new Rect(
-mResizeHandleThickness,
-mResizeHandleThickness,
mTaskWidth + mResizeHandleThickness,
0);
- touchRegion.union(topInputBounds);
+ mTouchRegion.union(topInputBounds);
final Rect leftInputBounds = new Rect(
-mResizeHandleThickness,
0,
0,
mTaskHeight);
- touchRegion.union(leftInputBounds);
+ mTouchRegion.union(leftInputBounds);
final Rect rightInputBounds = new Rect(
mTaskWidth,
0,
mTaskWidth + mResizeHandleThickness,
mTaskHeight);
- touchRegion.union(rightInputBounds);
+ mTouchRegion.union(rightInputBounds);
final Rect bottomInputBounds = new Rect(
-mResizeHandleThickness,
mTaskHeight,
mTaskWidth + mResizeHandleThickness,
mTaskHeight + mResizeHandleThickness);
- touchRegion.union(bottomInputBounds);
+ mTouchRegion.union(bottomInputBounds);
// Set up touch areas in each corner.
int cornerRadius = mCornerSize / 2;
@@ -231,28 +237,28 @@ class DragResizeInputListener implements AutoCloseable {
-cornerRadius,
cornerRadius,
cornerRadius);
- touchRegion.union(mLeftTopCornerBounds);
+ mTouchRegion.union(mLeftTopCornerBounds);
mRightTopCornerBounds = new Rect(
mTaskWidth - cornerRadius,
-cornerRadius,
mTaskWidth + cornerRadius,
cornerRadius);
- touchRegion.union(mRightTopCornerBounds);
+ mTouchRegion.union(mRightTopCornerBounds);
mLeftBottomCornerBounds = new Rect(
-cornerRadius,
mTaskHeight - cornerRadius,
cornerRadius,
mTaskHeight + cornerRadius);
- touchRegion.union(mLeftBottomCornerBounds);
+ mTouchRegion.union(mLeftBottomCornerBounds);
mRightBottomCornerBounds = new Rect(
mTaskWidth - cornerRadius,
mTaskHeight - cornerRadius,
mTaskWidth + cornerRadius,
mTaskHeight + cornerRadius);
- touchRegion.union(mRightBottomCornerBounds);
+ mTouchRegion.union(mRightBottomCornerBounds);
try {
mWindowSession.updateInputChannel(
@@ -262,7 +268,7 @@ class DragResizeInputListener implements AutoCloseable {
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
INPUT_FEATURE_SPY,
- touchRegion);
+ mTouchRegion);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -281,19 +287,8 @@ class DragResizeInputListener implements AutoCloseable {
// issue. However, were there touchscreen-only a region out of the task bounds, mouse
// gestures will become no-op in that region, even though the mouse gestures may appear to
// be performed on the input window behind the resize handle.
- touchRegion.op(0, 0, mTaskWidth, mTaskHeight, Region.Op.DIFFERENCE);
- try {
- mWindowSession.updateInputChannel(
- mSinkInputChannel.getToken(),
- mDisplayId,
- mInputSinkSurface,
- FLAG_NOT_FOCUSABLE,
- 0 /* privateFlags */,
- INPUT_FEATURE_NO_INPUT_CHANNEL,
- touchRegion);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ mTouchRegion.op(0, 0, mTaskWidth, mTaskHeight, Region.Op.DIFFERENCE);
+ updateSinkInputChannel(mTouchRegion);
return true;
}
@@ -309,19 +304,34 @@ class DragResizeInputListener implements AutoCloseable {
return region;
}
+ private void updateSinkInputChannel(Region region) {
+ try {
+ mWindowSession.updateInputChannel(
+ mSinkInputChannel.getToken(),
+ mDisplayId,
+ mInputSinkSurface,
+ FLAG_NOT_FOCUSABLE,
+ 0 /* privateFlags */,
+ INPUT_FEATURE_NO_INPUT_CHANNEL,
+ region);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+
@Override
public void close() {
mInputEventReceiver.dispose();
mInputChannel.dispose();
try {
- mWindowSession.remove(mFakeWindow);
+ mWindowSession.remove(mFakeWindow.asBinder());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
mSinkInputChannel.dispose();
try {
- mWindowSession.remove(mFakeSinkWindow);
+ mWindowSession.remove(mFakeSinkWindow.asBinder());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -337,6 +347,7 @@ class DragResizeInputListener implements AutoCloseable {
private boolean mConsumeBatchEventScheduled;
private boolean mShouldHandleEvents;
private int mLastCursorType = PointerIcon.TYPE_DEFAULT;
+ private Rect mDragStartTaskBounds;
private TaskResizeInputEventReceiver(
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -398,12 +409,15 @@ class DragResizeInputListener implements AutoCloseable {
}
if (mShouldHandleEvents) {
mInputManager.pilferPointers(mInputChannel.getToken());
-
mDragPointerId = e.getPointerId(0);
float rawX = e.getRawX(0);
float rawY = e.getRawY(0);
int ctrlType = calculateCtrlType(isTouch, x, y);
- mCallback.onDragPositioningStart(ctrlType, rawX, rawY);
+ mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
+ rawX, rawY);
+ // Increase the input sink region to cover the whole screen; this is to
+ // prevent input and focus from going to other tasks during a drag resize.
+ updateInputSinkRegionForDrag(mDragStartTaskBounds);
result = true;
}
break;
@@ -415,7 +429,8 @@ class DragResizeInputListener implements AutoCloseable {
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
float rawX = e.getRawX(dragPointerIndex);
float rawY = e.getRawY(dragPointerIndex);
- mCallback.onDragPositioningMove(rawX, rawY);
+ final Rect taskBounds = mCallback.onDragPositioningMove(rawX, rawY);
+ updateInputSinkRegionForDrag(taskBounds);
result = true;
break;
}
@@ -423,8 +438,13 @@ class DragResizeInputListener implements AutoCloseable {
case MotionEvent.ACTION_CANCEL: {
if (mShouldHandleEvents) {
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
- mCallback.onDragPositioningEnd(
+ final Rect taskBounds = mCallback.onDragPositioningEnd(
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ // If taskBounds has changed, setGeometry will be called and update the
+ // sink region. Otherwise, we should revert it here.
+ if (taskBounds.equals(mDragStartTaskBounds)) {
+ updateSinkInputChannel(mTouchRegion);
+ }
}
mShouldHandleEvents = false;
mDragPointerId = -1;
@@ -444,6 +464,18 @@ class DragResizeInputListener implements AutoCloseable {
return result;
}
+ private void updateInputSinkRegionForDrag(Rect taskBounds) {
+ final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId);
+ final Region dragTouchRegion = new Region(-taskBounds.left,
+ -taskBounds.top,
+ -taskBounds.left + layout.width(),
+ -taskBounds.top + layout.height());
+ // Remove the localized task bounds from the touch region.
+ taskBounds.offsetTo(0, 0);
+ dragTouchRegion.op(taskBounds, Region.Op.DIFFERENCE);
+ updateSinkInputChannel(dragTouchRegion);
+ }
+
private boolean isInCornerBounds(float xf, float yf) {
return calculateCornersCtrlType(xf, yf) != 0;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index dadd264596fb..bf11c8bc4f79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -68,7 +68,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
}
@Override
- public void onDragPositioningStart(int ctrlType, float x, float y) {
+ public Rect onDragPositioningStart(int ctrlType, float x, float y) {
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -87,6 +87,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
+ return mRepositionTaskBounds;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index b2267ddb6ba7..af055230b629 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -2,10 +2,12 @@ package com.android.wm.shell.windowdecor
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
import android.graphics.PointF
import android.graphics.Rect
import android.view.MotionEvent
import android.view.SurfaceControl
+import com.android.internal.policy.ScreenDecorationsUtils
/**
* Creates an animator to shrink and position task after a user drags a fullscreen task from
@@ -14,6 +16,7 @@ import android.view.SurfaceControl
* accessed by the EnterDesktopTaskTransitionHandler.
*/
class MoveToDesktopAnimator @JvmOverloads constructor(
+ private val context: Context,
private val startBounds: Rect,
private val taskInfo: RunningTaskInfo,
private val taskSurface: SurfaceControl,
@@ -33,9 +36,11 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
.setDuration(ANIMATION_DURATION.toLong())
.apply {
val t = SurfaceControl.Transaction()
+ val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
addUpdateListener { animation ->
val animatorValue = animation.animatedValue as Float
t.setScale(taskSurface, animatorValue, animatorValue)
+ .setCornerRadius(taskSurface, cornerRadius)
.apply()
}
}
@@ -44,19 +49,40 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
val position: PointF = PointF(0.0f, 0.0f)
/**
+ * Whether motion events from the drag gesture should affect the dragged surface or not. Used
+ * to disallow moving the surface's position prematurely since it should not start moving at
+ * all until the drag-to-desktop transition is ready to animate and the wallpaper/home are
+ * ready to be revealed behind the dragged/scaled task.
+ */
+ private var allowSurfaceChangesOnMove = false
+
+ /**
* Starts the animation that scales the task down.
*/
fun startAnimation() {
+ allowSurfaceChangesOnMove = true
dragToDesktopAnimator.start()
}
/**
- * Uses the position of the motion event and the current scale of the task as defined by the
- * ValueAnimator to update the local position variable and set the task surface's position
+ * Uses the position of the motion event of the drag-to-desktop gesture to update the dragged
+ * task's position on screen to follow the touch point. Note that the position change won't
+ * be applied immediately always, such as near the beginning where it waits until the wallpaper
+ * or home are visible behind it. Once they're visible the surface will catch-up to the most
+ * recent touch position.
*/
fun updatePosition(ev: MotionEvent) {
- position.x = ev.x - animatedTaskWidth / 2
- position.y = ev.y
+ // Using rawX/Y because when dragging a task in split, the local X/Y is relative to the
+ // split stages, but the split task surface is re-parented to the task display area to
+ // allow dragging beyond its stage across any region of the display. Because of that, the
+ // rawX/Y are more true to where the gesture is on screen and where the surface should be
+ // positioned.
+ position.x = ev.rawX - animatedTaskWidth / 2
+ position.y = ev.rawY
+
+ if (!allowSurfaceChangesOnMove) {
+ return
+ }
val t = transactionFactory()
t.setPosition(taskSurface, position.x, position.y)
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 852c037baad5..79fec0978a12 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
@@ -85,7 +85,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
}
@Override
- public void onDragPositioningStart(int ctrlType, float x, float y) {
+ public Rect onDragPositioningStart(int ctrlType, float x, float y) {
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
@@ -107,6 +107,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
mDisplayController.getDisplayLayout(mDesktopWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
+ return mRepositionTaskBounds;
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
index 406ada97a07d..5a017ad21044 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
@@ -63,11 +63,7 @@ data_sources: {
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker"
atrace_apps: "com.android.wm.shell.flicker.other"
- atrace_apps: "com.android.wm.shell.flicker.bubbles"
- atrace_apps: "com.android.wm.shell.flicker.pip"
- atrace_apps: "com.android.wm.shell.flicker.splitscreen"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
index 406ada97a07d..15998311e43a 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
@@ -63,11 +63,7 @@ data_sources: {
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker"
- atrace_apps: "com.android.wm.shell.flicker.other"
atrace_apps: "com.android.wm.shell.flicker.bubbles"
- atrace_apps: "com.android.wm.shell.flicker.pip"
- atrace_apps: "com.android.wm.shell.flicker.splitscreen"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 4d11dfb37c05..386983ce6aae 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -124,6 +124,10 @@ android_test {
":WMShellFlickerTestsPipCommon-src",
],
static_libs: ["WMShellFlickerTestsBase"],
+ test_suites: [
+ "device-tests",
+ "csuite",
+ ],
}
csuite_test {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 6df65391ea61..89ecc29d977b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -71,9 +71,9 @@
<!-- Enable mocking GPS location by the test app -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ value="appops set com.android.shell android:mock_location allow"/>
<option name="teardown-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ value="appops set com.android.shell android:mock_location deny"/>
</target_preparer>
<!-- Needed for pushing the trace config file -->
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index bd8b0056a6a3..182a9089d040 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -31,10 +31,18 @@ import org.junit.runners.Parameterized
abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
protected abstract val standardAppHelper: StandardAppHelper
+ protected abstract val permissions: Array<String>
+
@FlickerBuilderProvider
override fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withoutScreenRecorder()
+ instrumentation.uiAutomation.adoptShellPermissionIdentity()
+ for (permission in permissions) {
+ instrumentation.uiAutomation.grantRuntimePermission(
+ standardAppHelper.packageName,
+ permission
+ )
+ }
setup { flicker.scenario.setIsTablet(tapl.isTablet) }
transition()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 4da52ef1272c..d06cf775ca60 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.content.Context
import android.location.Criteria
import android.location.Location
@@ -64,6 +65,9 @@ import org.junit.runners.Parameterized
open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS,
+ Manifest.permission.ACCESS_FINE_LOCATION)
+
val locationManager: LocationManager =
instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val mainHandler = Handler(Looper.getMainLooper())
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 5498e8c4f970..32f12592135d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -62,6 +63,8 @@ import org.junit.runners.Parameterized
open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: NetflixAppHelper = NetflixAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
standardAppHelper.launchViaIntent(
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index d8afc25caf71..509b32c11afe 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.YouTubeAppHelper
@@ -58,6 +59,8 @@ import org.junit.runners.Parameterized
open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
standardAppHelper.launchViaIntent(
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
index 406ada97a07d..fc15ff9b9af8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
@@ -63,11 +63,7 @@ data_sources: {
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker"
- atrace_apps: "com.android.wm.shell.flicker.other"
- atrace_apps: "com.android.wm.shell.flicker.bubbles"
atrace_apps: "com.android.wm.shell.flicker.pip"
- atrace_apps: "com.android.wm.shell.flicker.splitscreen"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
index 406ada97a07d..9f2e49755fec 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
@@ -63,11 +63,7 @@ data_sources: {
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker"
- atrace_apps: "com.android.wm.shell.flicker.other"
- atrace_apps: "com.android.wm.shell.flicker.bubbles"
- atrace_apps: "com.android.wm.shell.flicker.pip"
- atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.android.wm.shell.flicker.service"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
index 406ada97a07d..b55f4ecdb6a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
@@ -63,10 +63,6 @@ data_sources: {
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker"
- atrace_apps: "com.android.wm.shell.flicker.other"
- atrace_apps: "com.android.wm.shell.flicker.bubbles"
- atrace_apps: "com.android.wm.shell.flicker.pip"
atrace_apps: "com.android.wm.shell.flicker.splitscreen"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
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 ebcb6407a6fd..fde6acb9bfe5 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
@@ -100,6 +100,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
@Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler
+ @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
@Mock lateinit var splitScreenController: SplitScreenController
@@ -127,7 +128,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
controller = createController()
- controller.splitScreenController = splitScreenController
+ controller.setSplitScreenController(splitScreenController)
shellInit.init()
@@ -150,6 +151,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
mToggleResizeDesktopTaskTransitionHandler,
+ dragToDesktopTransitionHandler,
desktopModeTaskRepository,
launchAdjacentController,
recentsTransitionHandler,
@@ -757,6 +759,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createSplitScreenTask(displayId)
+ whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
new file mode 100644
index 000000000000..a5629c8f8f15
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -0,0 +1,211 @@
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WindowingMode
+import android.graphics.PointF
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionInfo.FLAG_IS_WALLPAPER
+import androidx.test.filters.SmallTest
+import com.android.server.testutils.any
+import com.android.server.testutils.mock
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import java.util.function.Supplier
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/** Tests of [DragToDesktopTransitionHandler]. */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DragToDesktopTransitionHandlerTest : ShellTestCase() {
+
+ @Mock private lateinit var transitions: Transitions
+ @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var splitScreenController: SplitScreenController
+
+ private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+
+ private lateinit var handler: DragToDesktopTransitionHandler
+
+ @Before
+ fun setUp() {
+ handler =
+ DragToDesktopTransitionHandler(
+ context,
+ transitions,
+ taskDisplayAreaOrganizer,
+ transactionSupplier
+ )
+ .apply { setSplitScreenController(splitScreenController) }
+ }
+
+ @Test
+ fun startDragToDesktop_animateDragWhenReady() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+
+ // Now it's ready to animate.
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ verify(dragAnimator).startAnimation()
+ }
+
+ @Test
+ fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started and is ready to animate.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+
+ handler.cancelDragToDesktopTransition()
+
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ // Don't even animate the "drag" since it was already cancelled.
+ verify(dragAnimator, never()).startAnimation()
+ // Instead, start the cancel transition.
+ verify(transitions)
+ .startTransition(eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), any(), eq(handler))
+ }
+
+ @Test
+ fun cancelDragToDesktop_startWasReady_cancel() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ whenever(dragAnimator.position).thenReturn(PointF())
+ // Simulate transition is started and is ready to animate.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ // Then user cancelled after it had already started.
+ handler.cancelDragToDesktopTransition()
+
+ // Cancel animation should run since it had already started.
+ verify(dragAnimator).endAnimator()
+ }
+
+ @Test
+ fun cancelDragToDesktop_startWasNotReady_animateCancel() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started and is ready to animate.
+ startDragToDesktopTransition(task, dragAnimator)
+
+ // Then user cancelled before the transition was ready and animated.
+ handler.cancelDragToDesktopTransition()
+
+ // No need to animate the cancel since the start animation couldn't even start.
+ verifyZeroInteractions(dragAnimator)
+ }
+
+ private fun startDragToDesktopTransition(
+ task: RunningTaskInfo,
+ dragAnimator: MoveToDesktopAnimator
+ ): IBinder {
+ val token = mock<IBinder>()
+ whenever(
+ transitions.startTransition(
+ eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ )
+ .thenReturn(token)
+ handler.startDragToDesktopTransition(task.taskId, dragAnimator, mock())
+ return token
+ }
+
+ private fun createTask(
+ @WindowingMode windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+ isHome: Boolean = false,
+ ): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setActivityType(if (isHome) ACTIVITY_TYPE_HOME else ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(windowingMode)
+ .build()
+ .also {
+ whenever(splitScreenController.isTaskInSplitScreen(it.taskId))
+ .thenReturn(windowingMode == WINDOWING_MODE_MULTI_WINDOW)
+ }
+ }
+
+ private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo {
+ return TransitionInfo(type, 0 /* flags */).apply {
+ addChange( // Home.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo =
+ TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+ flags = flags or FLAG_IS_WALLPAPER
+ }
+ )
+ addChange( // Dragged Task.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo = draggedTask
+ }
+ )
+ addChange( // Wallpaper.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo = null
+ flags = flags or FLAG_IS_WALLPAPER
+ }
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
deleted file mode 100644
index 772d97d8eb32..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
+++ /dev/null
@@ -1,171 +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.desktopmode;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerToken;
-import android.window.TransitionInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
-
-import junit.framework.AssertionFailedError;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.Supplier;
-
-/** Tests of {@link com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler} */
-@SmallTest
-public class EnterDesktopTaskTransitionHandlerTest {
-
- @Mock
- private Transitions mTransitions;
- @Mock
- IBinder mToken;
- @Mock
- Supplier<SurfaceControl.Transaction> mTransactionFactory;
- @Mock
- SurfaceControl.Transaction mStartT;
- @Mock
- SurfaceControl.Transaction mFinishT;
- @Mock
- SurfaceControl.Transaction mAnimationT;
- @Mock
- Transitions.TransitionFinishCallback mTransitionFinishCallback;
- @Mock
- ShellExecutor mExecutor;
- @Mock
- SurfaceControl mSurfaceControl;
- @Mock
- MoveToDesktopAnimator mMoveToDesktopAnimator;
- @Mock
- PointF mPosition;
-
- private EnterDesktopTaskTransitionHandler mEnterDesktopTaskTransitionHandler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- doReturn(mExecutor).when(mTransitions).getMainExecutor();
- doReturn(mAnimationT).when(mTransactionFactory).get();
- doReturn(mPosition).when(mMoveToDesktopAnimator).getPosition();
-
- mEnterDesktopTaskTransitionHandler = new EnterDesktopTaskTransitionHandler(mTransitions,
- mTransactionFactory);
- }
-
- @Test
- public void testEnterFreeformAnimation() {
- final int taskId = 1;
- WindowContainerTransaction wct = new WindowContainerTransaction();
- doReturn(mToken).when(mTransitions)
- .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
- mEnterDesktopTaskTransitionHandler);
- doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();
-
- mEnterDesktopTaskTransitionHandler.startMoveToDesktop(wct,
- mMoveToDesktopAnimator, null);
-
- TransitionInfo.Change change =
- createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
- TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE,
- change);
-
-
- assertTrue(mEnterDesktopTaskTransitionHandler
- .startAnimation(mToken, info, mStartT, mFinishT, mTransitionFinishCallback));
-
- verify(mStartT).setWindowCrop(mSurfaceControl, null);
- verify(mStartT).apply();
- }
-
- @Test
- public void testTransitEnterDesktopModeAnimation() throws Throwable {
- final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE;
- final int taskId = 1;
- WindowContainerTransaction wct = new WindowContainerTransaction();
- doReturn(mToken).when(mTransitions)
- .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler);
- mEnterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct, null);
-
- TransitionInfo.Change change =
- createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
- change.setEndAbsBounds(new Rect(0, 0, 1, 1));
- TransitionInfo info = createTransitionInfo(
- Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change);
-
- runOnUiThread(() -> {
- try {
- assertTrue(mEnterDesktopTaskTransitionHandler
- .startAnimation(mToken, info, mStartT, mFinishT,
- mTransitionFinishCallback));
- } catch (Exception e) {
- throw new AssertionFailedError(e.getMessage());
- }
- });
-
- verify(mStartT).setWindowCrop(mSurfaceControl, change.getEndAbsBounds().width(),
- change.getEndAbsBounds().height());
- verify(mStartT).apply();
- }
-
- private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
- @WindowConfiguration.WindowingMode int windowingMode) {
- final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
- final TransitionInfo.Change change = new TransitionInfo.Change(
- new WindowContainerToken(mock(IWindowContainerToken.class)), mSurfaceControl);
- change.setMode(type);
- change.setTaskInfo(taskInfo);
- return change;
- }
-
- private static TransitionInfo createTransitionInfo(
- @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
- TransitionInfo info = new TransitionInfo(type, 0);
- info.addChange(change);
- return info;
- }
-
-}
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 4e2b7f6d16b2..800f9e4e5371 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
@@ -66,6 +66,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -283,7 +284,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
.round(any(), any(), anyBoolean());
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
- .scale(any(), any(), any(), any(), anyFloat());
+ .scale(any(), any(), any(), ArgumentMatchers.<Rect>any(), anyFloat());
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
.alpha(any(), any(), anyFloat());
doNothing().when(mMockPipSurfaceTransactionHelper).onDensityOrFontScaleChanged(any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 4e300d9a7e69..01c9bd0cb9f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -40,6 +40,8 @@ import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -93,6 +95,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
+import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -1463,6 +1467,43 @@ public class ShellTransitionTests extends ShellTestCase {
assertEquals(0, mDefaultHandler.activeCount());
}
+ @Test
+ public void testCloseTransitAnimationWhenClosingChangesExists() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false,
+ Transitions.TAG);
+ spyOn(transitionAnimation);
+
+ // Creating a transition by the app hooking the back key event to start the
+ // previous activity with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ // flags in order to clear the top activity and bring the exist previous activity to front.
+ // Expects the activity transition should playing the close animation instead the initiated
+ // open animation made by startActivity.
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_CLOSE).addChange(TRANSIT_TO_FRONT).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+
+ final int type = getTransitionTypeFromInfo(info);
+ assertEquals(TRANSIT_CLOSE, type);
+
+ TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(0), 0,
+ transitionAnimation, false);
+ verify(transitionAnimation).loadDefaultAnimationAttr(
+ eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean());
+
+ TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(1), 0,
+ transitionAnimation, false);
+ verify(transitionAnimation).loadDefaultAnimationAttr(
+ eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean());
+ }
+
class ChangeBuilder {
final TransitionInfo.Change mChange;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 7917ba6eeb41..6d73c12dc304 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.unfold;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +47,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
@@ -265,6 +267,42 @@ public class UnfoldTransitionHandlerTest {
verify(finishCallback).onTransitionFinished(any());
}
+ @Test
+ public void mergeAnimation_eatsDisplayOnlyTransitions() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+ TransitionFinishCallback mergeCallback = mock(TransitionFinishCallback.class);
+
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback);
+
+ // Offer a keyguard unlock transition - this should NOT merge
+ mUnfoldTransitionHandler.mergeAnimation(
+ new Binder(),
+ new TransitionInfoBuilder(TRANSIT_CHANGE, TRANSIT_FLAG_KEYGUARD_GOING_AWAY).build(),
+ mock(SurfaceControl.Transaction.class),
+ mTransition,
+ mergeCallback);
+ verify(finishCallback, never()).onTransitionFinished(any());
+
+ // Offer a CHANGE-only transition - this SHOULD merge (b/278064943)
+ mUnfoldTransitionHandler.mergeAnimation(
+ new Binder(),
+ new TransitionInfoBuilder(TRANSIT_CHANGE).build(),
+ mock(SurfaceControl.Transaction.class),
+ mTransition,
+ mergeCallback);
+ verify(mergeCallback).onTransitionFinished(any());
+
+ // We should never have finished the original transition.
+ verify(finishCallback, never()).onTransitionFinished(any());
+ }
+
private TransitionRequestInfo createUnfoldTransitionRequestInfo() {
ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
@@ -372,4 +410,4 @@ public class UnfoldTransitionHandlerTest {
private TransitionInfo createNonUnfoldTransitionInfo() {
return new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
}
-} \ No newline at end of file
+}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index da728f90e8e0..79a735786c38 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -553,6 +553,7 @@ cc_defaults {
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
+ "utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
"Animator.cpp",
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 31fc929dfcdf..008ea3aaa2e7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -589,10 +589,40 @@ void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Pain
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
+bool SkiaCanvas::useGainmapShader(Bitmap& bitmap) {
+ // If the bitmap doesn't have a gainmap, don't use the gainmap shader
+ if (!bitmap.hasGainmap()) return false;
+
+ // If we don't have an owned canvas, then we're either hardware accelerated or drawing
+ // to a picture - use the gainmap shader out of caution. Ideally a picture canvas would
+ // use a drawable here instead to defer making that decision until the last possible
+ // moment
+ if (!mCanvasOwned) return true;
+
+ auto info = mCanvasOwned->imageInfo();
+
+ // If it's an unknown colortype then it's not a bitmap-backed canvas
+ if (info.colorType() == SkColorType::kUnknown_SkColorType) return true;
+
+ skcms_TransferFunction tfn;
+ info.colorSpace()->transferFn(&tfn);
+
+ auto transferType = skcms_TransferFunction_getType(&tfn);
+ switch (transferType) {
+ case skcms_TFType_HLGish:
+ case skcms_TFType_HLGinvish:
+ case skcms_TFType_PQish:
+ return true;
+ case skcms_TFType_Invalid:
+ case skcms_TFType_sRGBish:
+ return false;
+ }
+}
+
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
@@ -619,7 +649,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index ced02241ffe2..4bf1790e2415 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -223,6 +223,8 @@ private:
void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode);
+ bool useGainmapShader(Bitmap& bitmap);
+
class Clip;
std::unique_ptr<SkCanvas> mCanvasOwned; // Might own a canvas we allocated.
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 34cb4aef70d9..f4ee36ec66d1 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -30,6 +30,7 @@
#include <minikin/MinikinExtent.h>
#include <minikin/MinikinPaint.h>
#include <minikin/MinikinRect.h>
+#include <utils/TypefaceUtils.h>
namespace android {
@@ -142,7 +143,7 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
skVariation[i].value = SkFloatToScalar(variations[i].value);
}
args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
- sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args));
return std::make_shared<MinikinFontSkia>(std::move(face), mSourceId, mFontData, mFontSize,
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index caffdfc907f7..ef4dce57bf46 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,19 +17,19 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
-#include "Typeface.h"
-
-#include <cutils/compiler.h>
-
#include <SkFont.h>
#include <SkPaint.h>
#include <SkSamplingOptions.h>
-#include <string>
-
-#include <minikin/FontFamily.h>
+#include <cutils/compiler.h>
#include <minikin/FamilyVariant.h>
+#include <minikin/FontFamily.h>
+#include <minikin/FontFeature.h>
#include <minikin/Hyphenator.h>
+#include <string>
+
+#include "Typeface.h"
+
namespace android {
class BlurDrawLooper;
@@ -82,11 +82,15 @@ public:
float getWordSpacing() const { return mWordSpacing; }
- void setFontFeatureSettings(const std::string& fontFeatureSettings) {
- mFontFeatureSettings = fontFeatureSettings;
+ void setFontFeatureSettings(std::string_view fontFeatures) {
+ mFontFeatureSettings = minikin::FontFeature::parse(fontFeatures);
}
- std::string getFontFeatureSettings() const { return mFontFeatureSettings; }
+ void resetFontFeatures() { mFontFeatureSettings.clear(); }
+
+ const std::vector<minikin::FontFeature>& getFontFeatureSettings() const {
+ return mFontFeatureSettings;
+ }
void setMinikinLocaleListId(uint32_t minikinLocaleListId) {
mMinikinLocaleListId = minikinLocaleListId;
@@ -170,7 +174,7 @@ private:
float mLetterSpacing = 0;
float mWordSpacing = 0;
- std::string mFontFeatureSettings;
+ std::vector<minikin::FontFeature> mFontFeatureSettings;
uint32_t mMinikinLocaleListId;
std::optional<minikin::FamilyVariant> mFamilyVariant;
uint32_t mHyphenEdit = 0;
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index 0c3af61fc089..e6d790f56d0f 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -31,6 +31,7 @@
#include <minikin/FontFamily.h>
#include <minikin/LocaleList.h>
#include <ui/FatVector.h>
+#include <utils/TypefaceUtils.h>
#include <memory>
@@ -125,7 +126,7 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, in
args.setCollectionIndex(ttcIndex);
args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
- sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == NULL) {
ALOGE("addFont failed to create font, invalid request");
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 8c71d6fc7860..d84b73d1a1ca 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -33,6 +33,7 @@
#include <cassert>
#include <cstring>
#include <memory>
+#include <string_view>
#include <vector>
#include "ColorFilter.h"
@@ -690,10 +691,11 @@ namespace PaintGlue {
jstring settings) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (!settings) {
- paint->setFontFeatureSettings(std::string());
+ paint->resetFontFeatures();
} else {
ScopedUtfChars settingsChars(env, settings);
- paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ paint->setFontFeatureSettings(
+ std::string_view(settingsChars.c_str(), settingsChars.size()));
}
}
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index a1b05c186ec0..a7d64231da80 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -597,7 +597,13 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
SkIRect clipBounds;
if (enableClip) {
uirenderer::Rect initialClipBounds;
- props.getClippingRectForFlags(props.getClippingFlags(), &initialClipBounds);
+ const auto clipFlags = props.getClippingFlags();
+ if (clipFlags) {
+ props.getClippingRectForFlags(clipFlags, &initialClipBounds);
+ } else {
+ // Works for RenderNode::damageSelf()
+ initialClipBounds.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ }
clipBounds =
info.damageAccumulator
->computeClipAndTransform(initialClipBounds.toSkRect(), &transform)
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 2ec94c954fe9..f405abaaf5b4 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -37,6 +37,7 @@
#include <minikin/LocaleList.h>
#include <minikin/SystemFonts.h>
#include <ui/FatVector.h>
+#include <utils/TypefaceUtils.h>
#include <memory>
@@ -459,7 +460,7 @@ std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
args.setCollectionIndex(ttcIndex);
args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
- sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == nullptr) {
return nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e706eb083ab6..d55d28d469d0 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -527,52 +527,65 @@ Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) {
return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge);
}
-struct DestroySemaphoreInfo {
+class SharedSemaphoreInfo : public LightRefBase<SharedSemaphoreInfo> {
PFN_vkDestroySemaphore mDestroyFunction;
VkDevice mDevice;
VkSemaphore mSemaphore;
+ GrBackendSemaphore mGrBackendSemaphore;
- DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
- VkSemaphore semaphore)
- : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {}
+ SharedSemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
+ VkSemaphore semaphore)
+ : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {
+ mGrBackendSemaphore.initVulkan(semaphore);
+ }
+
+ ~SharedSemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+
+ friend class LightRefBase<SharedSemaphoreInfo>;
+ friend class sp<SharedSemaphoreInfo>;
+
+public:
+ VkSemaphore semaphore() const { return mSemaphore; }
- ~DestroySemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+ GrBackendSemaphore* grBackendSemaphore() { return &mGrBackendSemaphore; }
};
static void destroy_semaphore(void* context) {
- DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context);
- delete info;
+ SharedSemaphoreInfo* info = reinterpret_cast<SharedSemaphoreInfo*>(context);
+ info->decStrong(0);
}
VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ATRACE_NAME("Vulkan finish frame");
- VkExportSemaphoreCreateInfo exportInfo;
- exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
- exportInfo.pNext = nullptr;
- exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkSemaphoreCreateInfo semaphoreInfo;
- semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreInfo.pNext = &exportInfo;
- semaphoreInfo.flags = 0;
- VkSemaphore semaphore;
- VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
- ALOGE_IF(VK_SUCCESS != err, "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
-
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
-
+ sp<SharedSemaphoreInfo> sharedSemaphore;
GrFlushInfo flushInfo;
- if (err == VK_SUCCESS) {
- flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
- flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext =
- new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
- } else {
- semaphore = VK_NULL_HANDLE;
+
+ {
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ ALOGE_IF(VK_SUCCESS != err,
+ "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
+
+ if (err == VK_SUCCESS) {
+ sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
+ flushInfo.fNumSemaphores = 1;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
+ flushInfo.fFinishedProc = destroy_semaphore;
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
+ }
}
+
GrDirectContext* context = GrAsDirectContext(surface->recordingContext());
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
@@ -581,37 +594,34 @@ VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
- if (semaphore != VK_NULL_HANDLE) {
- if (submitted == GrSemaphoresSubmitted::kYes) {
- if (mFrameBoundaryANDROID) {
- // retrieve VkImage used as render target
- VkImage image = VK_NULL_HANDLE;
- GrBackendRenderTarget backendRenderTarget =
- SkSurfaces::GetBackendRenderTarget(
- surface, SkSurfaces::BackendHandleAccess::kFlushRead);
- if (backendRenderTarget.isValid()) {
- GrVkImageInfo info;
- if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
- image = info.fImage;
- } else {
- ALOGE("Frame boundary: backend is not vulkan");
- }
+ if (sharedSemaphore) {
+ if (submitted == GrSemaphoresSubmitted::kYes && mFrameBoundaryANDROID) {
+ // retrieve VkImage used as render target
+ VkImage image = VK_NULL_HANDLE;
+ GrBackendRenderTarget backendRenderTarget = SkSurfaces::GetBackendRenderTarget(
+ surface, SkSurfaces::BackendHandleAccess::kFlushRead);
+ if (backendRenderTarget.isValid()) {
+ GrVkImageInfo info;
+ if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
+ image = info.fImage;
} else {
- ALOGE("Frame boundary: invalid backend render target");
+ ALOGE("Frame boundary: backend is not vulkan");
}
- // frameBoundaryANDROID needs to know about mSwapSemaphore, but
- // it won't wait on it.
- mFrameBoundaryANDROID(mDevice, semaphore, image);
+ } else {
+ ALOGE("Frame boundary: invalid backend render target");
}
+ // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+ // it won't wait on it.
+ mFrameBoundaryANDROID(mDevice, sharedSemaphore->semaphore(), image);
}
VkSemaphoreGetFdInfoKHR getFdInfo;
getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = semaphore;
+ getFdInfo.semaphore = sharedSemaphore->semaphore();
getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
int fenceFd = -1;
- err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
drawResult.presentFence.reset(fenceFd);
} else {
@@ -732,15 +742,15 @@ status_t VulkanManager::createReleaseFence(int* nativeFence, GrDirectContext* gr
return INVALID_OPERATION;
}
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
+ auto sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
// Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback
GrFlushInfo flushInfo;
flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
grContext->submit();
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 499afa039d1f..c71c4d243a8b 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -29,6 +29,7 @@
#include "hwui/MinikinSkia.h"
#include "hwui/Typeface.h"
+#include "utils/TypefaceUtils.h"
using namespace android;
@@ -56,7 +57,7 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
sk_sp<SkData> skData =
SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
- sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font =
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index db2be20936fb..c70a30477ecf 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -36,6 +36,7 @@
#include "hwui/MinikinUtils.h"
#include "hwui/Paint.h"
#include "hwui/Typeface.h"
+#include "utils/TypefaceUtils.h"
using namespace android;
@@ -66,7 +67,7 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
sk_sp<SkData> skData =
SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
- sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font =
diff --git a/libs/hwui/utils/TypefaceUtils.cpp b/libs/hwui/utils/TypefaceUtils.cpp
new file mode 100644
index 000000000000..a30b9257cd28
--- /dev/null
+++ b/libs/hwui/utils/TypefaceUtils.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include <utils/TypefaceUtils.h>
+
+#include "include/ports/SkFontMgr_empty.h"
+
+namespace android {
+
+sk_sp<SkFontMgr> FreeTypeFontMgr() {
+ static sk_sp<SkFontMgr> mgr = SkFontMgr_New_Custom_Empty();
+ return mgr;
+}
+
+} // namespace android
diff --git a/libs/hwui/utils/TypefaceUtils.h b/libs/hwui/utils/TypefaceUtils.h
new file mode 100644
index 000000000000..c0adeaea3c6c
--- /dev/null
+++ b/libs/hwui/utils/TypefaceUtils.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+
+namespace android {
+
+// Return an SkFontMgr which is capable of turning bytes into a SkTypeface using Freetype.
+// There are no other fonts inside this SkFontMgr (e.g. no system fonts).
+sk_sp<SkFontMgr> FreeTypeFontMgr();
+
+} // namespace android \ No newline at end of file
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 576ebc1579ef..65e16056c106 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -74,6 +74,14 @@ std::shared_ptr<PointerController> PointerController::create(
controller = std::shared_ptr<PointerController>(
new MousePointerController(policy, looper, spriteController, enabled));
break;
+ case ControllerType::TOUCH:
+ controller = std::shared_ptr<PointerController>(
+ new TouchPointerController(policy, looper, spriteController, enabled));
+ break;
+ case ControllerType::STYLUS:
+ controller = std::shared_ptr<PointerController>(
+ new StylusPointerController(policy, looper, spriteController, enabled));
+ break;
case ControllerType::LEGACY:
default:
controller = std::shared_ptr<PointerController>(
@@ -167,8 +175,7 @@ void PointerController::setPosition(float x, float y) {
FloatPoint PointerController::getPosition() const {
if (!mEnabled) {
- return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ return FloatPoint{0, 0};
}
const int32_t displayId = mCursorController.getDisplayId();
@@ -397,4 +404,34 @@ MousePointerController::MousePointerController(const sp<PointerControllerPolicyI
PointerController::setPresentation(Presentation::POINTER);
}
+MousePointerController::~MousePointerController() {
+ MousePointerController::fade(Transition::IMMEDIATE);
+}
+
+// --- TouchPointerController ---
+
+TouchPointerController::TouchPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ SpriteController& spriteController, bool enabled)
+ : PointerController(policy, looper, spriteController, enabled) {
+ PointerController::setPresentation(Presentation::SPOT);
+}
+
+TouchPointerController::~TouchPointerController() {
+ TouchPointerController::clearSpots();
+}
+
+// --- StylusPointerController ---
+
+StylusPointerController::StylusPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ SpriteController& spriteController, bool enabled)
+ : PointerController(policy, looper, spriteController, enabled) {
+ PointerController::setPresentation(Presentation::STYLUS_HOVER);
+}
+
+StylusPointerController::~StylusPointerController() {
+ StylusPointerController::fade(Transition::IMMEDIATE);
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 08e19a096d87..fa07c3989720 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -68,7 +68,7 @@ public:
void updatePointerIcon(PointerIconStyle iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+ virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
void reloadPointerResources();
void onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports);
@@ -143,6 +143,74 @@ public:
const sp<Looper>& looper, SpriteController& spriteController,
bool enabled);
+ ~MousePointerController() override;
+
+ void setPresentation(Presentation) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32, int32_t) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void clearSpots() override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+};
+
+class TouchPointerController : public PointerController {
+public:
+ /** A version of PointerController that controls touch spots. */
+ TouchPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled);
+
+ ~TouchPointerController() override;
+
+ std::optional<FloatRect> getBounds() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void move(float, float) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setPosition(float, float) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ FloatPoint getPosition() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ int32_t getDisplayId() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void fade(Transition) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void unfade(Transition) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setDisplayViewport(const DisplayViewport&) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setPresentation(Presentation) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void updatePointerIcon(PointerIconStyle) {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setCustomPointerIcon(const SpriteIcon&) {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ // fade() should not be called by inactivity timeout. Do nothing.
+ void setInactivityTimeout(InactivityTimeout) override {}
+};
+
+class StylusPointerController : public PointerController {
+public:
+ /** A version of PointerController that controls one stylus pointer. */
+ StylusPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled);
+
+ ~StylusPointerController() override;
+
void setPresentation(Presentation) override {
LOG_ALWAYS_FATAL("Should not be called");
}
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 5a274353f68e..2b349d498d59 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -325,7 +325,7 @@ public final class AudioDeviceAttributes implements Parcelable {
+ " role:" + roleToString(mRole)
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
: AudioSystem.getInputDeviceName(mNativeType))
- + " addr:" + mAddress
+ + " addr:" + Utils.anonymizeBluetoothAddress(mNativeType, mAddress)
+ " name:" + mName
+ " profiles:" + mAudioProfiles.toString()
+ " descriptors:" + mAudioDescriptors.toString());
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 9211c53a17bc..73bc6f96bf8b 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -156,7 +156,7 @@ public class AudioDevicePort extends AudioPort {
AudioSystem.getOutputDeviceName(mType));
return "{" + super.toString()
+ ", mType: " + type
- + ", mAddress: " + mAddress
+ + ", mAddress: " + Utils.anonymizeBluetoothAddress(mType, mAddress)
+ "}";
}
}
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 985a7584ffe2..0f48abeb6882 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -22,6 +22,8 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -54,6 +56,7 @@ public final class AudioHalVersionInfo implements Parcelable, Comparable<AudioHa
flag = false,
prefix = "AUDIO_HAL_TYPE_",
value = {AUDIO_HAL_TYPE_HIDL, AUDIO_HAL_TYPE_AIDL})
+ @Retention(RetentionPolicy.SOURCE)
public @interface AudioHalType {}
/** AudioHalVersionInfo object of all valid Audio HAL versions. */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5d211f460cc5..de842e68b118 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -19,8 +19,8 @@ package android.media;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
-
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
import android.Manifest;
@@ -2942,6 +2942,33 @@ public class AudioManager {
}
//====================================================================
+ // Loudness management
+ private final Object mLoudnessCodecLock = new Object();
+
+ @GuardedBy("mLoudnessCodecLock")
+ private LoudnessCodecDispatcher mLoudnessCodecDispatcher = null;
+
+ /**
+ * Creates a new instance of {@link LoudnessCodecConfigurator}.
+ * @return the {@link LoudnessCodecConfigurator} instance
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public @NonNull LoudnessCodecConfigurator createLoudnessCodecConfigurator() {
+ LoudnessCodecConfigurator configurator;
+ synchronized (mLoudnessCodecLock) {
+ // initialize lazily
+ if (mLoudnessCodecDispatcher == null) {
+ mLoudnessCodecDispatcher = new LoudnessCodecDispatcher(this);
+ }
+ configurator = mLoudnessCodecDispatcher.createLoudnessCodecConfigurator();
+ }
+ return configurator;
+ }
+
+ //====================================================================
// Bluetooth SCO control
/**
* Sticky broadcast intent action indicating that the Bluetooth SCO audio
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7faa13c80c62..447d3bbddceb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1176,6 +1176,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
case AudioFormat.ENCODING_PCM_FLOAT:
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
+ case AudioFormat.ENCODING_E_AC3_JOC:
mAudioFormat = audioFormat;
break;
default:
@@ -1188,20 +1189,12 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
// Convenience method for the contructor's audio buffer size check.
- // preconditions:
- // mChannelCount is valid
- // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
- // or AudioFormat.ENCODING_PCM_FLOAT
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
- // NB: this section is only valid with PCM data.
- // To update when supporting compressed formats
- int frameSizeInBytes = mChannelCount
- * (AudioFormat.getBytesPerSample(mAudioFormat));
- if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
+ if ((audioBufferSize % getFormat().getFrameSizeInBytes() != 0) || (audioBufferSize < 1)) {
throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
- + " (frame size " + frameSizeInBytes + ")");
+ + " (frame size " + getFormat().getFrameSizeInBytes() + ")");
}
mNativeBufferSizeInBytes = audioBufferSize;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8584dbc62ef9..11e3a088b5e2 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -37,6 +37,7 @@ import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IDevicesForAttributesCallback;
+import android.media.ILoudnessCodecUpdatesDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IPreferredMixerAttributesDispatcher;
@@ -51,6 +52,7 @@ import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerOutputCallback;
import android.media.IStreamAliasingDispatcher;
import android.media.IVolumeController;
+import android.media.LoudnessCodecFormat;
import android.media.PlayerBase;
import android.media.VolumeInfo;
import android.media.VolumePolicy;
@@ -303,7 +305,7 @@ interface IAudioService {
void disableSafeMediaVolume(String callingPackage);
- void lowerVolumeToRs1(String callingPackage);
+ oneway void lowerVolumeToRs1(String callingPackage);
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
float getOutputRs2UpperBound();
@@ -728,4 +730,20 @@ interface IAudioService {
@EnforcePermission("MODIFY_AUDIO_ROUTING")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
boolean isBluetoothVariableLatencyEnabled();
+
+ void registerLoudnessCodecUpdatesDispatcher(in ILoudnessCodecUpdatesDispatcher dispatcher);
+
+ void unregisterLoudnessCodecUpdatesDispatcher(in ILoudnessCodecUpdatesDispatcher dispatcher);
+
+ oneway void startLoudnessCodecUpdates(in int piid);
+
+ oneway void stopLoudnessCodecUpdates(in int piid);
+
+ oneway void addLoudnesssCodecFormat(in int piid, in LoudnessCodecFormat format);
+
+ oneway void addLoudnesssCodecFormatList(in int piid, in List<LoudnessCodecFormat> format);
+
+ oneway void removeLoudnessCodecFormat(in int piid, in LoudnessCodecFormat format);
+
+ PersistableBundle getLoudnessParams(in int piid, in LoudnessCodecFormat format);
}
diff --git a/media/java/android/media/ILoudnessCodecUpdatesDispatcher.aidl b/media/java/android/media/ILoudnessCodecUpdatesDispatcher.aidl
new file mode 100644
index 000000000000..16eaaea38b7f
--- /dev/null
+++ b/media/java/android/media/ILoudnessCodecUpdatesDispatcher.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.media;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface which provides updates for the clients about MediaCodec loudness
+ * parameter changes.
+ *
+ * {@hide}
+ */
+oneway interface ILoudnessCodecUpdatesDispatcher {
+
+ void dispatchLoudnessCodecParameterChange(int piid, in PersistableBundle params);
+
+} \ No newline at end of file
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
new file mode 100644
index 000000000000..409abc211cb6
--- /dev/null
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -0,0 +1,225 @@
+/*
+ * 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.media;
+
+import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * Class for getting recommended loudness parameter updates for audio decoders, according to the
+ * encoded format and current audio routing. Those updates can be automatically applied to the
+ * {@link MediaCodec} instance(s), or be provided to the user. The codec loudness management
+ * updates are defined by the CTA-2075 standard.
+ * <p>A new object should be instantiated for each {@link AudioTrack} with the help
+ * of {@link AudioManager#createLoudnessCodecConfigurator()}.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+public class LoudnessCodecConfigurator {
+ private static final String TAG = "LoudnessCodecConfigurator";
+
+ /**
+ * Listener used for receiving asynchronous loudness metadata updates.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public interface OnLoudnessCodecUpdateListener {
+ /**
+ * Contains the MediaCodec key/values that can be set directly to
+ * configure the loudness of the handle's corresponding decoder (see
+ * {@link MediaCodec#setParameters(Bundle)}).
+ *
+ * @param mediaCodec the mediaCodec that will receive the new parameters
+ * @param codecValues contains loudness key/value pairs that can be set
+ * directly on the mediaCodec. The listener can modify
+ * these values with their own edits which will be
+ * returned for the mediaCodec configuration
+ * @return a Bundle which contains the original computed codecValues
+ * aggregated with user edits. The platform will configure the associated
+ * MediaCodecs with the returned Bundle params.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ @NonNull
+ default Bundle onLoudnessCodecUpdate(@NonNull MediaCodec mediaCodec,
+ @NonNull Bundle codecValues) {
+ return codecValues;
+ }
+ }
+
+ @NonNull private final LoudnessCodecDispatcher mLcDispatcher;
+
+ private AudioTrack mAudioTrack;
+
+ private final List<MediaCodec> mMediaCodecs = new ArrayList<>();
+
+ /** @hide */
+ protected LoudnessCodecConfigurator(@NonNull LoudnessCodecDispatcher lcDispatcher) {
+ mLcDispatcher = Objects.requireNonNull(lcDispatcher);
+ }
+
+
+ /**
+ * Starts receiving asynchronous loudness updates and registers the listener for
+ * receiving {@link MediaCodec} loudness parameter updates.
+ * <p>This method should be called before {@link #startLoudnessCodecUpdates()} or
+ * after {@link #stopLoudnessCodecUpdates()}.
+ *
+ * @param executor {@link Executor} to handle the callbacks
+ * @param listener used to receive updates
+ *
+ * @return {@code true} if there is at least one {@link MediaCodec} and
+ * {@link AudioTrack} set and the user can expect receiving updates.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public boolean startLoudnessCodecUpdates(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnLoudnessCodecUpdateListener listener) {
+ Objects.requireNonNull(executor,
+ "Executor must not be null");
+ Objects.requireNonNull(listener,
+ "OnLoudnessCodecUpdateListener must not be null");
+ mLcDispatcher.addLoudnessCodecListener(this, executor, listener);
+
+ return checkStartLoudnessConfigurator();
+ }
+
+ /**
+ * Starts receiving asynchronous loudness updates.
+ * <p>The registered MediaCodecs will be updated automatically without any client
+ * callbacks.
+ *
+ * @return {@code true} if there is at least one MediaCodec and AudioTrack set
+ * (see {@link #setAudioTrack(AudioTrack)}, {@link #addMediaCodec(MediaCodec)})
+ * and the user can expect receiving updates.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public boolean startLoudnessCodecUpdates() {
+ mLcDispatcher.addLoudnessCodecListener(this,
+ Executors.newSingleThreadExecutor(), new OnLoudnessCodecUpdateListener() {});
+ return checkStartLoudnessConfigurator();
+ }
+
+ /**
+ * Stops receiving asynchronous loudness updates.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public void stopLoudnessCodecUpdates() {
+ mLcDispatcher.removeLoudnessCodecListener(this);
+ }
+
+ /**
+ * Adds a new {@link MediaCodec} that will stream data to an {@link AudioTrack}
+ * which is registered through {@link #setAudioTrack(AudioTrack)}.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
+ mMediaCodecs.add(Objects.requireNonNull(mediaCodec,
+ "MediaCodec for addMediaCodec must not be null"));
+ }
+
+ /**
+ * Removes the {@link MediaCodec} from receiving loudness updates.
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public void removeMediaCodec(@NonNull MediaCodec mediaCodec) {
+ mMediaCodecs.remove(Objects.requireNonNull(mediaCodec,
+ "MediaCodec for removeMediaCodec must not be null"));
+ }
+
+ /**
+ * Sets the {@link AudioTrack} that can receive audio data from the added
+ * {@link MediaCodec}'s. The {@link AudioTrack} is used to determine the devices
+ * on which the streaming will take place and hence will directly influence the
+ * loudness params.
+ * <p>Should be called before starting the loudness updates
+ * (see {@link #startLoudnessCodecUpdates()},
+ * {@link #startLoudnessCodecUpdates(Executor, OnLoudnessCodecUpdateListener)})
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public void setAudioTrack(@NonNull AudioTrack audioTrack) {
+ mAudioTrack = Objects.requireNonNull(audioTrack,
+ "AudioTrack for setAudioTrack must not be null");
+ }
+
+ /**
+ * Gets synchronous loudness updates when no listener is required and at least one
+ * {@link MediaCodec} which streams to a registered {@link AudioTrack} is set.
+ * Otherwise, an empty {@link Bundle} will be returned.
+ *
+ * @return the {@link Bundle} containing the current loudness parameters. Caller is
+ * responsible to update the {@link MediaCodec}
+ *
+ * TODO: remove hide once API is final
+ * @hide
+ */
+ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
+ @NonNull
+ public Bundle getLoudnessCodecParams(@NonNull MediaCodec mediaCodec) {
+ // TODO: implement synchronous loudness params updates
+ return new Bundle();
+ }
+
+ private boolean checkStartLoudnessConfigurator() {
+ if (mAudioTrack == null) {
+ Log.w(TAG, "Cannot start loudness configurator without an AudioTrack");
+ return false;
+ }
+
+ if (mMediaCodecs.isEmpty()) {
+ Log.w(TAG, "Cannot start loudness configurator without at least one MediaCodec");
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/media/java/android/media/LoudnessCodecDispatcher.java b/media/java/android/media/LoudnessCodecDispatcher.java
new file mode 100644
index 000000000000..fc5c354b98f5
--- /dev/null
+++ b/media/java/android/media/LoudnessCodecDispatcher.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Class used to handle the loudness related communication with the audio service.
+ * @hide
+ */
+public class LoudnessCodecDispatcher {
+ private final class LoudnessCodecUpdatesDispatcherStub
+ extends ILoudnessCodecUpdatesDispatcher.Stub
+ implements CallbackUtil.DispatcherStub {
+ @Override
+ public void dispatchLoudnessCodecParameterChange(int piid, PersistableBundle params) {
+ mLoudnessListenerMgr.callListeners(listener ->
+ mConfiguratorListener.computeIfPresent(listener, (l, c) -> {
+ // TODO: send the bundle for the user to update
+ return c;
+ }));
+ }
+
+ @Override
+ public void register(boolean register) {
+ try {
+ if (register) {
+ mAm.getService().registerLoudnessCodecUpdatesDispatcher(this);
+ } else {
+ mAm.getService().unregisterLoudnessCodecUpdatesDispatcher(this);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
+ mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
+
+ @NonNull private final LoudnessCodecUpdatesDispatcherStub mLoudnessCodecStub;
+
+ private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>
+ mConfiguratorListener = new HashMap<>();
+
+ @NonNull private final AudioManager mAm;
+
+ protected LoudnessCodecDispatcher(@NonNull AudioManager am) {
+ mAm = Objects.requireNonNull(am);
+ mLoudnessCodecStub = new LoudnessCodecUpdatesDispatcherStub();
+ }
+
+ /** @hide */
+ public LoudnessCodecConfigurator createLoudnessCodecConfigurator() {
+ return new LoudnessCodecConfigurator(this);
+ }
+
+ /** @hide */
+ public void addLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnLoudnessCodecUpdateListener listener) {
+ Objects.requireNonNull(configurator);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+
+ mConfiguratorListener.put(listener, configurator);
+ mLoudnessListenerMgr.addListener(
+ executor, listener, "addLoudnessCodecListener", () -> mLoudnessCodecStub);
+ }
+
+ /** @hide */
+ public void removeLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator) {
+ Objects.requireNonNull(configurator);
+
+ for (Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e :
+ mConfiguratorListener.entrySet()) {
+ if (e.getValue() == configurator) {
+ final OnLoudnessCodecUpdateListener listener = e.getKey();
+ mConfiguratorListener.remove(listener);
+ mLoudnessListenerMgr.removeListener(listener, "removeLoudnessCodecListener");
+ break;
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/LoudnessCodecFormat.aidl b/media/java/android/media/LoudnessCodecFormat.aidl
new file mode 100644
index 000000000000..75c906060d43
--- /dev/null
+++ b/media/java/android/media/LoudnessCodecFormat.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+
+/**
+ * Loudness format which specifies the input attributes used for measuring
+ * the parameters required to perform loudness alignment as specified by the
+ * CTA2075 standard.
+ *
+ * {@hide}
+ */
+parcelable LoudnessCodecFormat {
+ String metadataType;
+ boolean isDownmixing;
+} \ No newline at end of file
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index faa7f7fa3aa1..5331046dd0e3 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -2018,6 +2018,8 @@ public final class MediaDrm implements AutoCloseable {
* {@link #HDCP_V2_1},
* {@link #HDCP_V2_2},
* {@link #HDCP_V2_3}
+ *
+ * @removed mistakenly exposed previously
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@@ -2121,6 +2123,8 @@ public final class MediaDrm implements AutoCloseable {
* {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
* {@link #SECURITY_LEVEL_HW_SECURE_DECODE},
* {@link #SECURITY_LEVEL_HW_SECURE_ALL}
+ *
+ * @removed mistakenly exposed previously
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 5509782e2988..f52297450e85 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -148,6 +148,7 @@ public class MediaMetrics {
createKey("headTrackerEnabled", String.class); // spatializer
public static final Key<Integer> INDEX = createKey("index", Integer.class); // volume
+ public static final Key<Integer> OLD_INDEX = createKey("oldIndex", Integer.class); // volume
public static final Key<Integer> INPUT_PORT_COUNT =
createKey("inputPortCount", Integer.class); // MIDI
// Either "true" or "false"
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8230a825ea0c..bde0c0cb2a9b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -759,13 +759,7 @@ public final class MediaRouter2 {
return;
}
- synchronized (mLock) {
- mRoutes.clear();
- for (MediaRoute2Info route : currentRoutes) {
- mRoutes.put(route.getId(), route);
- }
- updateFilteredRoutesLocked();
- }
+ updateRoutesOnHandler(currentRoutes);
RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo();
mSystemController.setRoutingSessionInfo(currentSystemSessionInfo);
@@ -824,10 +818,10 @@ public final class MediaRouter2 {
}
}
- void updateRoutesOnHandler(List<MediaRoute2Info> routes) {
+ void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) {
synchronized (mLock) {
mRoutes.clear();
- for (MediaRoute2Info route : routes) {
+ for (MediaRoute2Info route : newRoutes) {
mRoutes.put(route.getId(), route);
}
updateFilteredRoutesLocked();
@@ -2703,16 +2697,6 @@ public final class MediaRouter2 {
notifyRouteListingPreferenceUpdated(routeListingPreference);
}
- private void onRoutesUpdatedOnHandler(@NonNull List<MediaRoute2Info> routes) {
- synchronized (mLock) {
- mRoutes.clear();
- for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getId(), route);
- }
- updateFilteredRoutesLocked();
- }
- }
-
private void onRequestFailedOnHandler(int requestId, int reason) {
MediaRouter2Manager.TransferRequest matchingRequest = null;
for (MediaRouter2Manager.TransferRequest request : mTransferRequests) {
@@ -2786,9 +2770,7 @@ public final class MediaRouter2 {
public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
mHandler.sendMessage(
obtainMessage(
- ProxyMediaRouter2Impl::onRoutesUpdatedOnHandler,
- ProxyMediaRouter2Impl.this,
- routes));
+ MediaRouter2::updateRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 74ca4b858ff6..99fcaf29688c 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -267,20 +267,26 @@ public class Spatializer {
HEAD_TRACKING_MODE_DISABLED,
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingMode {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingMode {};
/** @hide */
@IntDef(flag = false, value = {
HEAD_TRACKING_MODE_DISABLED,
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingModeSet {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingModeSet {};
/** @hide */
@IntDef(flag = false, value = {
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingModeSupported {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingModeSupported {};
/**
* @hide
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index ecb6b3d495ad..d07f6118f6f4 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -657,4 +657,35 @@ public class Utils {
// on the fly.
private final boolean mForceRemoveConsistency; // default false
}
+
+ /**
+ * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
+ * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
+ * @param address MAC address to be anonymized
+ * @return anonymized MAC address
+ */
+ public static @Nullable String anonymizeBluetoothAddress(@Nullable String address) {
+ if (address == null) {
+ return null;
+ }
+ if (address.length() != "AA:BB:CC:DD:EE:FF".length()) {
+ return address;
+ }
+ return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
+ }
+
+ /**
+ * Convert a Bluetooth MAC address to an anonymized one if the internal device type corresponds
+ * to a Bluetooth.
+ * @param deviceType the internal type of the audio device
+ * @param address MAC address to be anonymized
+ * @return anonymized MAC address
+ */
+ public static @Nullable String anonymizeBluetoothAddress(
+ int deviceType, @Nullable String address) {
+ if (!AudioSystem.isBluetoothDevice(deviceType)) {
+ return address;
+ }
+ return anonymizeBluetoothAddress(address);
+ }
}
diff --git a/media/java/android/media/tv/ad/ITvAdManager.aidl b/media/java/android/media/tv/ad/ITvAdManager.aidl
new file mode 100644
index 000000000000..92cc923dc9ab
--- /dev/null
+++ b/media/java/android/media/tv/ad/ITvAdManager.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.media.tv.ad;
+
+/**
+ * Interface to the TV AD service.
+ * @hide
+ */
+interface ITvAdManager {
+ void startAdService(in IBinder sessionToken, int userId);
+}
diff --git a/media/java/android/media/tv/ad/ITvAdSession.aidl b/media/java/android/media/tv/ad/ITvAdSession.aidl
new file mode 100644
index 000000000000..b834f1b9fb92
--- /dev/null
+++ b/media/java/android/media/tv/ad/ITvAdSession.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.media.tv.ad;
+
+/**
+ * Sub-interface of ITvAdService which is created per session and has its own context.
+ * @hide
+ */
+oneway interface ITvAdSession {
+ void startAdService();
+}
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
new file mode 100644
index 000000000000..aa5a290346b0
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.media.tv.ad;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Central system API to the overall client-side TV AD architecture, which arbitrates interaction
+ * between applications and AD services.
+ * @hide
+ */
+public class TvAdManager {
+ private static final String TAG = "TvAdManager";
+
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ public TvAdManager(ITvAdManager service, int userId) {
+ mService = service;
+ mUserId = userId;
+ }
+
+ /**
+ * The Session provides the per-session functionality of AD service.
+ */
+ public static final class Session {
+ private final IBinder mToken;
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ private Session(IBinder token, ITvAdManager service, int userId) {
+ mToken = token;
+ mService = service;
+ mUserId = userId;
+ }
+
+ void startAdService() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.startAdService(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java
new file mode 100644
index 000000000000..61101f07923c
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdService.java
@@ -0,0 +1,60 @@
+/*
+ * 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.media.tv.ad;
+
+import android.app.Service;
+import android.view.KeyEvent;
+
+/**
+ * The TvAdService class represents a TV client-side advertisement service.
+ * @hide
+ */
+public abstract class TvAdService extends Service {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvAdService";
+
+ /**
+ * Base class for derived classes to implement to provide a TV AD session.
+ */
+ public abstract static class Session implements KeyEvent.Callback {
+ /**
+ * Starts TvAdService session.
+ */
+ public void onStartAdService() {
+ }
+
+ void startAdService() {
+ onStartAdService();
+ }
+ }
+
+ /**
+ * Implements the internal ITvAdService interface.
+ */
+ public static class ITvAdSessionWrapper extends ITvAdSession.Stub {
+ private final Session mSessionImpl;
+
+ public ITvAdSessionWrapper(Session mSessionImpl) {
+ this.mSessionImpl = mSessionImpl;
+ }
+
+ @Override
+ public void startAdService() {
+ mSessionImpl.startAdService();
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.aidl b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
new file mode 100644
index 000000000000..a5e631fc8426
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+parcelable TvAdServiceInfo;
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.java b/media/java/android/media/tv/ad/TvAdServiceInfo.java
new file mode 100644
index 000000000000..ed04f1f9058c
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.java
@@ -0,0 +1,194 @@
+/*
+ * 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.media.tv.ad;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import androidx.annotation.NonNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV AD service.
+ * @hide
+ */
+public final class TvAdServiceInfo implements Parcelable {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvAdServiceInfo";
+
+ private static final String XML_START_TAG_NAME = "tv-ad-service";
+
+ private final ResolveInfo mService;
+ private final String mId;
+ private final List<String> mTypes = new ArrayList<>();
+
+ /**
+ * Constructs a TvAdServiceInfo object.
+ *
+ * @param context the application context
+ * @param component the component name of the TvAdService
+ */
+ public TvAdServiceInfo(@NonNull Context context, @NonNull ComponentName component) {
+ if (context == null) {
+ throw new IllegalArgumentException("context cannot be null.");
+ }
+ // TODO: use a constant
+ Intent intent = new Intent("android.media.tv.ad.TvAdService").setComponent(component);
+ ResolveInfo resolveInfo = context.getPackageManager().resolveService(
+ intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ if (resolveInfo == null) {
+ throw new IllegalArgumentException("Invalid component. Can't find the service.");
+ }
+
+ ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ String id;
+ id = generateAdServiceId(componentName);
+ List<String> types = new ArrayList<>();
+ parseServiceMetadata(resolveInfo, context, types);
+
+ mService = resolveInfo;
+ mId = id;
+ }
+
+ private TvAdServiceInfo(ResolveInfo service, String id, List<String> types) {
+ mService = service;
+ mId = id;
+ mTypes.addAll(types);
+ }
+
+ private TvAdServiceInfo(@NonNull Parcel in) {
+ mService = ResolveInfo.CREATOR.createFromParcel(in);
+ mId = in.readString();
+ in.readStringList(mTypes);
+ }
+
+ public static final Creator<TvAdServiceInfo> CREATOR = new Creator<TvAdServiceInfo>() {
+ @Override
+ public TvAdServiceInfo createFromParcel(Parcel in) {
+ return new TvAdServiceInfo(in);
+ }
+
+ @Override
+ public TvAdServiceInfo[] newArray(int size) {
+ return new TvAdServiceInfo[size];
+ }
+ };
+
+ /**
+ * Returns a unique ID for this TV AD service. The ID is generated from the package and class
+ * name implementing the TV AD service.
+ */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the component of the TV AD service.
+ * @hide
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+ }
+
+ /**
+ * Returns the information of the service that implements this AD service.
+ */
+ @Nullable
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Gets supported TV AD types.
+ */
+ @NonNull
+ public List<String> getSupportedTypes() {
+ return mTypes;
+ }
+
+ private static String generateAdServiceId(ComponentName name) {
+ return name.flattenToShortString();
+ }
+
+ private static void parseServiceMetadata(
+ ResolveInfo resolveInfo, Context context, List<String> types) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ PackageManager pm = context.getPackageManager();
+ // TODO: use constant for the metadata
+ try (XmlResourceParser parser =
+ serviceInfo.loadXmlMetaData(pm, "android.media.tv.ad.service")) {
+ if (parser == null) {
+ throw new IllegalStateException(
+ "No " + "android.media.tv.ad.service"
+ + " meta-data found for " + serviceInfo.name);
+ }
+
+ Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // move to the START_TAG
+ }
+
+ String nodeName = parser.getName();
+ if (!XML_START_TAG_NAME.equals(nodeName)) {
+ throw new IllegalStateException("Meta-data does not start with "
+ + XML_START_TAG_NAME + " tag for " + serviceInfo.name);
+ }
+
+ // TODO: parse attributes
+ } catch (IOException | XmlPullParserException e) {
+ throw new IllegalStateException(
+ "Failed reading meta-data for " + serviceInfo.packageName, e);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("No resources found for " + serviceInfo.packageName, e);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mService.writeToParcel(dest, flags);
+ dest.writeString(mId);
+ dest.writeStringList(mTypes);
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
new file mode 100644
index 000000000000..1a3771a9f24c
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.ViewGroup;
+
+/**
+ * Displays contents of TV AD services.
+ * @hide
+ */
+public class TvAdView extends ViewGroup {
+ private static final String TAG = "TvAdView";
+ private static final boolean DEBUG = false;
+
+ // TODO: create session
+ private TvAdManager.Session mSession;
+
+ public TvAdView(Context context) {
+ super(context, /* attrs = */null, /* defStyleAttr = */0);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onLayout (left=" + l + ", top=" + t + ", right=" + r + ", bottom=" + b + ",)");
+ }
+ }
+
+ /**
+ * Starts the AD service.
+ */
+ public void startAdService() {
+ if (DEBUG) {
+ Log.d(TAG, "start");
+ }
+ if (mSession != null) {
+ mSession.startAdService();
+ }
+ }
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index feb914fe3161..757e9f8e41b1 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -630,7 +630,6 @@ void FilterClientCallbackImpl::getMediaEvent(const jobjectArray& arr, const int
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
ScopedLocalRef<jobject> audioDescriptor(env);
- gAudioPresentationFields.init(env);
ScopedLocalRef presentationsJObj(env, JAudioPresentationInfo::asJobject(
env, gAudioPresentationFields));
switch (mediaEvent.extraMetaData.getTag()) {
@@ -3731,6 +3730,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V");
gFields.linearBlockSetInternalStateID =
env->GetMethodID(linearBlockClazz, "setInternalStateLocked", "(JZ)V");
+ gAudioPresentationFields.init(env);
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index fd394fc43477..7c710982e4f6 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -22,5 +22,15 @@
],
"file_patterns": ["performance_hint.cpp"]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsThermalTestCases",
+ "file_patterns": ["thermal.cpp"]
+ },
+ {
+ "name": "NativeThermalUnitTestCases",
+ "file_patterns": ["thermal.cpp"]
+ }
]
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b0af09c19b7e..fea6c5f95358 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -327,6 +327,7 @@ LIBANDROID {
AThermal_registerThermalStatusListener; # introduced=30
AThermal_unregisterThermalStatusListener; # introduced=30
AThermal_getThermalHeadroom; # introduced=31
+ AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream
APerformanceHint_getManager; # introduced=Tiramisu
APerformanceHint_createSession; # introduced=Tiramisu
APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
@@ -341,6 +342,7 @@ LIBANDROID {
LIBANDROID_PLATFORM {
global:
+ AThermal_setIThermalServiceForTesting;
APerformanceHint_setIHintManagerForTesting;
APerformanceHint_sendHint;
APerformanceHint_getThreadIds;
diff --git a/native/android/tests/thermal/Android.bp b/native/android/tests/thermal/Android.bp
new file mode 100644
index 000000000000..8540d8327ada
--- /dev/null
+++ b/native/android/tests/thermal/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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"],
+}
+
+cc_test {
+ name: "NativeThermalUnitTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["NativeThermalUnitTest.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libbinder",
+ "libpowermanager",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+}
diff --git a/native/android/tests/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp
new file mode 100644
index 000000000000..6d6861a3026a
--- /dev/null
+++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NativeThermalUnitTest"
+
+#include <android/os/IThermalService.h>
+#include <android/thermal.h>
+#include <binder/IBinder.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thermal_private.h>
+
+using android::binder::Status;
+
+using namespace testing;
+using namespace android;
+using namespace android::os;
+
+class MockIThermalService : public IThermalService {
+public:
+ MOCK_METHOD(Status, registerThermalEventListener,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, registerThermalEventListenerWithType,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener, int32_t type,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalEventListener,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getCurrentTemperatures,
+ (::std::vector<::android::os::Temperature> * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentTemperaturesWithType,
+ (int32_t type, ::std::vector<::android::os::Temperature>* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, registerThermalStatusListener,
+ (const ::android::sp<::android::os::IThermalStatusListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalStatusListener,
+ (const ::android::sp<::android::os::IThermalStatusListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getCurrentThermalStatus, (int32_t * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentCoolingDevices,
+ (::std::vector<::android::os::CoolingDevice> * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentCoolingDevicesWithType,
+ (int32_t type, ::std::vector<::android::os::CoolingDevice>* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getThermalHeadroom, (int32_t forecastSeconds, float* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return),
+ (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+class NativeThermalUnitTest : public Test {
+public:
+ void SetUp() override {
+ mMockIThermalService = new StrictMock<MockIThermalService>();
+ AThermal_setIThermalServiceForTesting(mMockIThermalService);
+ mThermalManager = AThermal_acquireManager();
+ }
+
+ void TearDown() override {
+ AThermal_setIThermalServiceForTesting(nullptr);
+ AThermal_releaseManager(mThermalManager);
+ }
+
+ StrictMock<MockIThermalService>* mMockIThermalService = nullptr;
+ AThermalManager* mThermalManager = nullptr;
+};
+
+static void checkThermalHeadroomThresholds(const std::vector<float>& expected,
+ const AThermalHeadroomThreshold* thresholds,
+ size_t size) {
+ if (thresholds == nullptr) {
+ FAIL() << "Unexpected null thresholds pointer";
+ }
+ for (int i = 0; i < (int)size; i++) {
+ auto t = thresholds[i];
+ ASSERT_EQ(i, t.thermalStatus) << "threshold " << i << " should have status " << i;
+ ASSERT_EQ(expected[i], t.headroom)
+ << "threshold " << i << " should have headroom " << expected[i];
+ }
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) {
+ std::vector<float> expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status())));
+ const AThermalHeadroomThreshold* thresholds1 = nullptr;
+ size_t size1;
+ ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1));
+ checkThermalHeadroomThresholds(expected, thresholds1, size1);
+ // following calls should be cached
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0);
+
+ const AThermalHeadroomThreshold* thresholds2 = nullptr;
+ size_t size2;
+ ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2));
+ checkThermalHeadroomThresholds(expected, thresholds2, size2);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithServerError) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_ILLEGAL_ARGUMENT)));
+ ASSERT_EQ(EPIPE, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size));
+ ASSERT_EQ(nullptr, thresholds);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithFeatureDisabled) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(Status::fromExceptionCode(
+ binder::Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ ASSERT_EQ(ENOSYS, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size));
+ ASSERT_EQ(nullptr, thresholds);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNullPtr) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ size_t* nullSize = nullptr;
+ ASSERT_EQ(EINVAL,
+ AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, nullSize));
+ ASSERT_EQ(nullptr, thresholds);
+ ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, nullptr, &size));
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmptyPtr) {
+ const AThermalHeadroomThreshold* initialized = new AThermalHeadroomThreshold[1];
+ size_t size;
+ ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size));
+ delete[] initialized;
+}
diff --git a/native/android/tests/thermal/OWNERS b/native/android/tests/thermal/OWNERS
new file mode 100644
index 000000000000..e3bbee92057d
--- /dev/null
+++ b/native/android/tests/thermal/OWNERS
@@ -0,0 +1 @@
+include /ADPF_OWNERS
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index 1f6ef4755aff..b43f2f16a7cb 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -16,27 +16,32 @@
#define LOG_TAG "thermal"
-#include <cerrno>
-#include <thread>
-#include <limits>
-
-#include <android/thermal.h>
+#include <android-base/thread_annotations.h>
#include <android/os/BnThermalStatusListener.h>
#include <android/os/IThermalService.h>
+#include <android/thermal.h>
#include <binder/IServiceManager.h>
+#include <thermal_private.h>
#include <utils/Log.h>
+#include <cerrno>
+#include <limits>
+#include <thread>
+
using android::sp;
using namespace android;
using namespace android::os;
struct ThermalServiceListener : public BnThermalStatusListener {
- public:
- virtual binder::Status onStatusChange(int32_t status) override;
- ThermalServiceListener(AThermalManager *manager) {mMgr = manager;}
- private:
- AThermalManager *mMgr;
+public:
+ virtual binder::Status onStatusChange(int32_t status) override;
+ ThermalServiceListener(AThermalManager *manager) {
+ mMgr = manager;
+ }
+
+private:
+ AThermalManager *mMgr;
};
struct ListenerCallback {
@@ -44,22 +49,29 @@ struct ListenerCallback {
void* data;
};
+static IThermalService *gIThermalServiceForTesting = nullptr;
+
struct AThermalManager {
- public:
- static AThermalManager* createAThermalManager();
- AThermalManager() = delete;
- ~AThermalManager();
- status_t notifyStateChange(int32_t status);
- status_t getCurrentThermalStatus(int32_t *status);
- status_t addListener(AThermal_StatusCallback, void *data);
- status_t removeListener(AThermal_StatusCallback, void *data);
- status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
- private:
- AThermalManager(sp<IThermalService> service);
- sp<IThermalService> mThermalSvc;
- sp<ThermalServiceListener> mServiceListener;
- std::vector<ListenerCallback> mListeners;
- std::mutex mMutex;
+public:
+ static AThermalManager *createAThermalManager();
+ AThermalManager() = delete;
+ ~AThermalManager();
+ status_t notifyStateChange(int32_t status);
+ status_t getCurrentThermalStatus(int32_t *status);
+ status_t addListener(AThermal_StatusCallback, void *data);
+ status_t removeListener(AThermal_StatusCallback, void *data);
+ status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
+ status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size);
+
+private:
+ AThermalManager(sp<IThermalService> service);
+ sp<IThermalService> mThermalSvc;
+ std::mutex mListenerMutex;
+ sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex);
+ std::vector<ListenerCallback> mListeners GUARDED_BY(mListenerMutex);
+ std::mutex mThresholdsMutex;
+ const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex)
+ size_t mThresholdsCount GUARDED_BY(mThresholdsMutex);
};
binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
@@ -70,6 +82,9 @@ binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
}
AThermalManager* AThermalManager::createAThermalManager() {
+ if (gIThermalServiceForTesting) {
+ return new AThermalManager(gIThermalServiceForTesting);
+ }
sp<IBinder> binder =
defaultServiceManager()->checkService(String16("thermalservice"));
@@ -81,12 +96,10 @@ AThermalManager* AThermalManager::createAThermalManager() {
}
AThermalManager::AThermalManager(sp<IThermalService> service)
- : mThermalSvc(service),
- mServiceListener(nullptr) {
-}
+ : mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
AThermalManager::~AThermalManager() {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> listenerLock(mListenerMutex);
mListeners.clear();
if (mServiceListener != nullptr) {
@@ -94,10 +107,13 @@ AThermalManager::~AThermalManager() {
mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
mServiceListener = nullptr;
}
+ listenerLock.unlock();
+ std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ delete[] mThresholds;
}
status_t AThermalManager::notifyStateChange(int32_t status) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
for (auto listener : mListeners) {
@@ -107,7 +123,7 @@ status_t AThermalManager::notifyStateChange(int32_t status) {
}
status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
if (callback == nullptr) {
// Callback can not be nullptr
@@ -141,7 +157,7 @@ status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *da
}
status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
auto it = std::remove_if(mListeners.begin(),
mListeners.end(),
@@ -198,6 +214,32 @@ status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *res
return OK;
}
+status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result,
+ size_t *size) {
+ std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ if (mThresholds == nullptr) {
+ auto thresholds = std::make_unique<std::vector<float>>();
+ binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
+ if (!ret.isOk()) {
+ if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ // feature is not enabled
+ return ENOSYS;
+ }
+ return EPIPE;
+ }
+ mThresholdsCount = thresholds->size();
+ auto t = new AThermalHeadroomThreshold[mThresholdsCount];
+ for (int i = 0; i < (int)mThresholdsCount; i++) {
+ t[i].headroom = (*thresholds)[i];
+ t[i].thermalStatus = static_cast<AThermalStatus>(i);
+ }
+ mThresholds = t;
+ }
+ *size = mThresholdsCount;
+ *result = mThresholds;
+ return OK;
+}
+
/**
* Acquire an instance of the thermal manager. This must be freed using
* {@link AThermal_releaseManager}.
@@ -291,14 +333,24 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
* threshold. Returns NaN if the device does not support this functionality or if
* this function is called significantly faster than once per second.
*/
-float AThermal_getThermalHeadroom(AThermalManager *manager,
- int forecastSeconds) {
+float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) {
float result = 0.0f;
status_t ret = manager->getThermalHeadroom(forecastSeconds, &result);
-
if (ret != OK) {
result = std::numeric_limits<float>::quiet_NaN();
}
-
return result;
}
+
+int AThermal_getThermalHeadroomThresholds(AThermalManager *manager,
+ const AThermalHeadroomThreshold **outThresholds,
+ size_t *size) {
+ if (outThresholds == nullptr || *outThresholds != nullptr || size == nullptr) {
+ return EINVAL;
+ }
+ return manager->getThermalHeadroomThresholds(outThresholds, size);
+}
+
+void AThermal_setIThermalServiceForTesting(void *iThermalService) {
+ gIThermalServiceForTesting = static_cast<IThermalService *>(iThermalService);
+}
diff --git a/nfc/Android.bp b/nfc/Android.bp
new file mode 100644
index 000000000000..bf9f47ceb0a7
--- /dev/null
+++ b/nfc/Android.bp
@@ -0,0 +1,51 @@
+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"],
+}
+
+filegroup {
+ name: "framework-nfc-non-updatable-sources",
+ path: "java",
+ srcs: [],
+}
+
+filegroup {
+ name: "framework-nfc-updatable-sources",
+ path: "java",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ exclude_srcs: [
+ ":framework-nfc-non-updatable-sources",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-nfc",
+ libs: [
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ ],
+ srcs: [
+ ":framework-nfc-updatable-sources",
+ ],
+ defaults: ["framework-non-updatable-unbundled-defaults"],
+ permitted_packages: [
+ "android.nfc",
+ "com.android.nfc",
+ ],
+ hidden_api_packages: [
+ "com.android.nfc",
+ ],
+ aidl: {
+ include_dirs: [
+ // TODO (b/303286040): Remove these when we change to |framework-module-defaults|
+ "frameworks/base/nfc/java",
+ "frameworks/base/core/java",
+ ],
+ },
+}
diff --git a/nfc/OWNERS b/nfc/OWNERS
new file mode 100644
index 000000000000..35e9713f5715
--- /dev/null
+++ b/nfc/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/TEST_MAPPING b/nfc/TEST_MAPPING
new file mode 100644
index 000000000000..5b5ea3790010
--- /dev/null
+++ b/nfc/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "NfcManagerTests"
+ },
+ {
+ "name": "CtsNfcTestCases"
+ }
+ ]
+}
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/module-lib-current.txt b/nfc/api/module-lib-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/module-lib-removed.txt b/nfc/api/module-lib-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/removed.txt b/nfc/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/system-removed.txt b/nfc/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/test-current.txt b/nfc/api/test-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/test-removed.txt b/nfc/api/test-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/nfc/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/java/android/nfc/Placeholder.java b/nfc/java/android/nfc/Placeholder.java
new file mode 100644
index 000000000000..3509644ac106
--- /dev/null
+++ b/nfc/java/android/nfc/Placeholder.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * Placeholder class so new framework-nfc module isn't empty, will be removed once module is
+ * populated.
+ *
+ * @hide
+ *
+ */
+public class Placeholder {
+}
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index b6f182dc7096..b84be40705fc 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -57,7 +57,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string>
<string name="permission_notification" msgid="693762568127741203">"Notificacions"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacions"</string>
- <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reproducció en continu"</string>
+ <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reproducció en línia"</string>
<string name="permission_phone_summary" msgid="6684396967861278044">"Pot fer i gestionar trucades"</string>
<string name="permission_call_logs_summary" msgid="6186103394658755022">"Pot llegir i escriure el registre de trucades del telèfon"</string>
<string name="permission_sms_summary" msgid="3508442683678912017">"Pot enviar i consultar missatges SMS"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index e130074843ff..e5d9c2881ea3 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -26,7 +26,7 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"gailua"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailu baterako baino gehiagotarako zerbitzuak"</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailuarteko zerbitzuak"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
index 87045cb689ea..c5fd89c015f2 100644
--- a/packages/CredentialManager/res/values-it/strings.xml
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -20,7 +20,7 @@
<string name="app_name" msgid="4539824758261855508">"Gestore delle credenziali"</string>
<string name="string_cancel" msgid="6369133483981306063">"Annulla"</string>
<string name="string_continue" msgid="1346732695941131882">"Continua"</string>
- <string name="string_more_options" msgid="2763852250269945472">"Salva un altro modo"</string>
+ <string name="string_more_options" msgid="2763852250269945472">"Salva in altro modo"</string>
<string name="string_learn_more" msgid="4541600451688392447">"Scopri di più"</string>
<string name="content_description_show_password" msgid="3283502010388521607">"Mostra password"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"Nascondi password"</string>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
index 33b0b3ae4336..7f63f10494ba 100644
--- a/packages/CredentialManager/res/values-nb/strings.xml
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -39,10 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Vi går mot en fremtid uten passord, men passord fortsetter å være tilgjengelige ved siden av passnøkler."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Velg hvor du vil lagre <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Velg et verktøy for passordlagring for å lagre informasjonen din og logge på raskere neste gang"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du opprette en tilgangsnøkkel for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du opprette en passnøkkel for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vil du lagre passord for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vil du lagre påloggingsinformasjon for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="passkey" msgid="632353688396759522">"tilgangsnøkkel"</string>
+ <string name="passkey" msgid="632353688396759522">"passnøkkel"</string>
<string name="password" msgid="6738570945182936667">"passord"</string>
<string name="passkeys" msgid="5733880786866559847">"passnøkler"</string>
<string name="passwords" msgid="5419394230391253816">"passord"</string>
@@ -61,7 +61,7 @@
<string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord"</string>
<string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passnøkler"</string>
<string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>-legitimasjon"</string>
- <string name="passkey_before_subtitle" msgid="2448119456208647444">"Tilgangsnøkkel"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"Passnøkkel"</string>
<string name="another_device" msgid="5147276802037801217">"En annen enhet"</string>
<string name="other_password_manager" msgid="565790221427004141">"Andre løsninger for passordlagring"</string>
<string name="close_sheet" msgid="1393792015338908262">"Lukk arket"</string>
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 98ad22c8060e..42f1207c69cb 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -19,35 +19,46 @@ package com.android.credentialmanager
import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
+import android.util.Log
+import com.android.credentialmanager.ktx.appLabel
+import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.mapper.toGet
-import com.android.credentialmanager.mapper.toRequestCancel
-import com.android.credentialmanager.mapper.toRequestClose
import com.android.credentialmanager.model.Request
fun Intent.parse(
packageManager: PackageManager,
- previousIntent: Intent? = null,
): Request {
- this.toRequestClose(previousIntent)?.let { closeRequest ->
- return closeRequest
- }
-
- this.toRequestCancel(packageManager)?.let { cancelRequest ->
- return cancelRequest
- }
+ return parseCancelUiRequest(packageManager)
+ ?: parseRequestInfo()
+}
- return when (requestInfo?.type) {
- RequestInfo.TYPE_CREATE -> {
- Request.Create
+fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
+ this.cancelUiRequest?.let { cancelUiRequest ->
+ val showCancel = cancelUiRequest.shouldShowCancellationUi().apply {
+ Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this")
}
-
- RequestInfo.TYPE_GET -> {
- this.toGet()
+ if (showCancel) {
+ val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+ if (appLabel == null) {
+ Log.d(TAG, "Received UI cancel request with an invalid package name.")
+ null
+ } else {
+ Request.Cancel(appName = appLabel, token = cancelUiRequest.token)
+ }
+ } else {
+ Request.Close(cancelUiRequest.token)
}
+ }
- else -> {
- throw IllegalStateException("Unrecognized request type: ${requestInfo?.type}")
+fun Intent.parseRequestInfo(): Request =
+ requestInfo.let{ info ->
+ when (info?.type) {
+ RequestInfo.TYPE_CREATE -> Request.Create(info.token)
+ RequestInfo.TYPE_GET -> toGet()
+ else -> {
+ throw IllegalStateException("Unrecognized request type: ${info?.type}")
+ }
}
}
-}
+
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
new file mode 100644
index 000000000000..49387cf410ac
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.client
+
+import android.content.Intent
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.UserSelectionDialogResult
+import com.android.credentialmanager.model.Request
+import kotlinx.coroutines.flow.StateFlow
+
+interface CredentialManagerClient {
+ /** The UI should monitor the request update. */
+ val requests: StateFlow<Request?>
+
+ /** The UI got a new intent; update the request state. */
+ fun updateRequest(intent: Intent)
+
+ /** Sends an error encountered during the UI. */
+ fun sendError(
+ @BaseDialogResult.ResultCode resultCode: Int,
+ errorMessage: String? = null,
+ )
+
+ /**
+ * Sends a response to the system service. The response
+ * contains information about the user's choice from the selector
+ * UI and the result of the provider operation launched with
+ * that selection.
+ *
+ * If the user choice was a normal entry, then the UI can finish
+ * the activity immediately. Otherwise if it was an authentication
+ * (locked) entry, then the UI will need to stay up and wait for
+ * a new intent from the system containing the new data for
+ * display.
+ *
+ * Note that if the provider operation returns RESULT_CANCELED,
+ * then the selector should not send that result back, and instead
+ * re-display the options to allow a user to have another choice.
+ *
+ * @throws [IllegalStateException] if [requests] is not [Request.Get].
+ */
+ fun sendResult(result: UserSelectionDialogResult)
+} \ 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
new file mode 100644
index 000000000000..83183b5f58eb
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.credentialmanager.client.impl
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.UserSelectionDialogResult
+import android.os.Bundle
+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 kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import javax.inject.Inject
+
+class CredentialManagerClientImpl @Inject constructor(
+ private val packageManager: PackageManager,
+) : CredentialManagerClient {
+
+ private val _requests = MutableStateFlow<Request?>(null)
+ override val requests: StateFlow<Request?> = _requests
+
+
+ override fun updateRequest(intent: Intent) {
+ val request = intent.parse(
+ packageManager = packageManager,
+ )
+ 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) {
+ Log.w(TAG, "drop terminate request for previous session.")
+ return
+ }
+ }
+ _requests.value = request
+ }
+
+ override fun sendError(resultCode: Int, errorMessage: String?) {
+ TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
+ }
+
+ override fun sendResult(result: UserSelectionDialogResult) {
+ val currentRequest = requests.value
+ check(currentRequest is Request.Get) { "current request is not get." }
+ currentRequest.resultReceiver?.let { receiver ->
+ val resultDataBundle = Bundle()
+ UserSelectionDialogResult.addToBundle(result, resultDataBundle)
+ receiver.send(
+ BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+ resultDataBundle
+ )
+ }
+ }
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
deleted file mode 100644
index 99dc9ec38eff..000000000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ /dev/null
@@ -1,36 +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.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.mapper
-
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.util.Log
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.ktx.appLabel
-import com.android.credentialmanager.ktx.cancelUiRequest
-import com.android.credentialmanager.model.Request
-
-fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
- this.cancelUiRequest?.let { cancelUiRequest ->
- val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
- if (appLabel == null) {
- Log.d(TAG, "Received UI cancel request with an invalid package name.")
- null
- } else {
- Request.Cancel(appName = appLabel)
- }
- }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
deleted file mode 100644
index 02ee77bc5ec3..000000000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.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.mapper
-
-import android.content.Intent
-import com.android.credentialmanager.ktx.cancelUiRequest
-import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.model.Request
-
-fun Intent.toRequestClose(
- previousIntent: Intent? = null,
-): Request.Close? {
- // Close request comes as "Cancel" request from Credential Manager API
- this.cancelUiRequest?.let { cancelUiRequest ->
-
- if (cancelUiRequest.shouldShowCancellationUi()) {
- // Current request is to Cancel and not to Close
- return null
- }
-
- previousIntent?.let {
- val previousToken = previousIntent.requestInfo?.token
- val currentToken = this.requestInfo?.token
-
- if (previousToken != currentToken) {
- // Current cancellation is for a different request, don't close the current flow.
- return null
- }
- }
-
- return Request.Close
- }
-
- return null
-} \ No newline at end of file
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 ed98f3ed5154..2289ed7320ca 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -25,33 +25,40 @@ import com.google.common.collect.ImmutableMap
/**
* Represents the request made by the CredentialManager API.
*/
-sealed class Request {
+sealed class Request private constructor(
+ open val token: IBinder?,
+) {
/**
* Request to close the app without displaying a message to the user and without reporting
* anything back to the Credential Manager service.
*/
- data object Close : Request()
+ data class Close(
+ override val token: IBinder?,
+ ) : Request(token)
/**
* Request to close the app, displaying a message to the user.
*/
data class Cancel(
- val appName: String
- ) : Request()
+ val appName: String,
+ override val token: IBinder?,
+ ) : Request(token)
/**
* Request to start the get credentials flow.
*/
data class Get(
- val token: IBinder?,
+ override val token: IBinder?,
val resultReceiver: ResultReceiver?,
val providers: ImmutableMap<String, ProviderData>,
val passwordEntries: ImmutableList<Password>,
- ) : Request()
-
+ ) : Request(token)
/**
* Request to start the create credentials flow.
*/
- data object Create : Request()
+ data class Create(
+ override val token: IBinder?,
+ ) : Request(token)
}
+
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
deleted file mode 100644
index 5738feea0772..000000000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
+++ /dev/null
@@ -1,57 +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.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.repository
-
-import android.content.Intent
-import android.credentials.ui.BaseDialogResult
-import android.credentials.ui.ProviderPendingIntentResponse
-import android.credentials.ui.UserSelectionDialogResult
-import android.os.Bundle
-import android.util.Log
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.model.Password
-import com.android.credentialmanager.model.Request
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class PasswordRepository @Inject constructor() {
-
- suspend fun selectPassword(
- password: Password,
- request: Request.Get,
- resultCode: Int? = null,
- resultData: Intent? = null,
- ) {
- Log.d(TAG, "password selected: {provider=${password.providerId}" +
- ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
-
- val userSelectionDialogResult = UserSelectionDialogResult(
- request.token,
- password.providerId,
- password.entry.key,
- password.entry.subkey,
- if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
- )
- val resultDataBundle = Bundle()
- UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
- request.resultReceiver?.send(
- BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
- resultDataBundle
- )
- }
-}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
deleted file mode 100644
index 1973fc175c1f..000000000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
+++ /dev/null
@@ -1,48 +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.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.repository
-
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.util.Log
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.parse
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class RequestRepository @Inject constructor(
- private val packageManager: PackageManager,
-) {
-
- private val _requests = MutableStateFlow<Request?>(null)
- val requests: StateFlow<Request?> = _requests
-
- suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
- val request = intent.parse(
- packageManager = packageManager,
- previousIntent = previousIntent
- )
-
- Log.d(TAG, "Request parsed: $request")
-
- _requests.value = request
- }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index f2df64aee22e..0df40d7adba5 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -25,6 +25,7 @@ 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() {
@@ -34,25 +35,21 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
@OptIn(ExperimentalHorologistApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
setTheme(android.R.style.Theme_DeviceDefault)
setContent {
MaterialTheme {
WearApp(
viewModel = viewModel,
- onCloseApp = ::finish,
+ onCloseApp = { exitProcess(0) },
)
}
}
- viewModel.onNewIntent(intent)
+ viewModel.updateRequest(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
-
- val previousIntent = getIntent()
setIntent(intent)
-
- viewModel.onNewIntent(intent, previousIntent)
+ viewModel.updateRequest(intent)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 435cd377114d..2a7e9e16a10e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -20,28 +20,27 @@ import android.content.Intent
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.client.CredentialManagerClient
import com.android.credentialmanager.ui.mappers.toGet
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class CredentialSelectorViewModel @Inject constructor(
- private val requestRepository: RequestRepository,
+ private val credentialManagerClient: CredentialManagerClient,
) : ViewModel() {
- val uiState: StateFlow<CredentialSelectorUiState> = requestRepository.requests
+ val uiState: StateFlow<CredentialSelectorUiState> = credentialManagerClient.requests
.map { request ->
when (request) {
null -> CredentialSelectorUiState.Idle
is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
- Request.Close -> CredentialSelectorUiState.Close
- Request.Create -> CredentialSelectorUiState.Create
+ is Request.Close -> CredentialSelectorUiState.Close
+ is Request.Create -> CredentialSelectorUiState.Create
is Request.Get -> request.toGet()
}
}
@@ -51,10 +50,8 @@ class CredentialSelectorViewModel @Inject constructor(
initialValue = CredentialSelectorUiState.Idle,
)
- fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
- viewModelScope.launch {
- requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
- }
+ fun updateRequest(intent: Intent) {
+ credentialManagerClient.updateRequest(intent = intent)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
index cb1a4a13690e..6ededf3411bf 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -2,17 +2,28 @@ package com.android.credentialmanager.di
import android.content.Context
import android.content.pm.PackageManager
+import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.client.impl.CredentialManagerClientImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
@Module
@InstallIn(SingletonComponent::class)
internal object AppModule {
@Provides
+ @Singleton
@JvmStatic
fun providePackageManager(@ApplicationContext context: Context): PackageManager =
- context.packageManager
+ context.packageManager
+
+ @Provides
+ @Singleton
+ @JvmStatic
+ fun provideCredentialManagerClient(packageManager: PackageManager): CredentialManagerClient =
+ CredentialManagerClientImpl(packageManager)
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
index da5697dab8d4..77fb3e7dfd9c 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -19,9 +19,14 @@ package com.android.credentialmanager.ui
import androidx.navigation.NavController
fun NavController.navigateToLoading() {
- navigate(Screen.Loading.route)
+ navigateToAsRoot(Screen.Loading.route)
}
fun NavController.navigateToSinglePasswordScreen() {
- navigate(Screen.SinglePasswordScreen.route)
+ navigateToAsRoot(Screen.SinglePasswordScreen.route)
+}
+
+fun NavController.navigateToAsRoot(route: String) {
+ popBackStack()
+ navigate(route)
}
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
index 43514a039661..fb72c544c978 100644
--- 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
@@ -17,6 +17,8 @@
package com.android.credentialmanager.ui.screens.single.password
import android.content.Intent
+import android.credentials.ui.ProviderPendingIntentResponse
+import android.credentials.ui.UserSelectionDialogResult
import android.util.Log
import androidx.activity.result.IntentSenderRequest
import androidx.annotation.MainThread
@@ -26,20 +28,17 @@ import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.getIntentSenderRequest
import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.repository.PasswordRepository
-import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.client.CredentialManagerClient
import com.android.credentialmanager.ui.model.PasswordUiModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class SinglePasswordScreenViewModel @Inject constructor(
- private val requestRepository: RequestRepository,
- private val passwordRepository: PasswordRepository,
+ private val credentialManagerClient: CredentialManagerClient,
) : ViewModel() {
private var initializeCalled = false
@@ -57,8 +56,8 @@ class SinglePasswordScreenViewModel @Inject constructor(
initializeCalled = true
viewModelScope.launch {
- val request = requestRepository.requests.first()
- Log.d(TAG, "request: $request")
+ val request = credentialManagerClient.requests.value
+ Log.d(TAG, "request: $request, client instance: $credentialManagerClient")
if (request !is Request.Get) {
_uiState.value = SinglePasswordScreenUiState.Error
@@ -93,16 +92,15 @@ class SinglePasswordScreenViewModel @Inject constructor(
resultCode: Int? = null,
resultData: Intent? = null,
) {
- viewModelScope.launch {
- passwordRepository.selectPassword(
- password = password,
- request = requestGet,
- resultCode = resultCode,
- resultData = resultData
- )
-
- _uiState.value = SinglePasswordScreenUiState.Completed
- }
+ val userSelectionDialogResult = UserSelectionDialogResult(
+ requestGet.token,
+ password.providerId,
+ password.entry.key,
+ password.entry.subkey,
+ if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+ )
+ credentialManagerClient.sendResult(userSelectionDialogResult)
+ _uiState.value = SinglePasswordScreenUiState.Completed
}
}
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 5b239d434fa4..2a375529c3c7 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -19,7 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajčiarske (nemčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
- <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnica"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bulharské, fonetické"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"talianske"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánske"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"nórske"</string>
@@ -27,7 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"fínske"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"chorvátske"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"české"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Český štýl QWERTY"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"české, štýl QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estónske"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"maďarské"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandské"</string>
@@ -36,18 +36,18 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
- <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turečtina F"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turecké, F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabské"</string>
- <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gréčtina"</string>
- <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrejčina"</string>
- <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litovčina"</string>
- <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Španielčina (Latinská Amerika)"</string>
- <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyština"</string>
- <string name="keyboard_layout_persian" msgid="3920643161015888527">"Perzština"</string>
+ <string name="keyboard_layout_greek" msgid="7289253560162386040">"grécke"</string>
+ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejské"</string>
+ <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litovské"</string>
+ <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španielske (Latinská Amerika)"</string>
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lotyšské"</string>
+ <string name="keyboard_layout_persian" msgid="3920643161015888527">"perzské"</string>
<string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbajdžanské"</string>
- <string name="keyboard_layout_polish" msgid="1121588624094925325">"Poľština"</string>
- <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bieloruština"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolčina"</string>
- <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzínčina"</string>
+ <string name="keyboard_layout_polish" msgid="1121588624094925325">"poľské"</string>
+ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bieloruské"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolské"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínske"</string>
</resources>
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 58224b817a4d..38bd7d5f3944 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -46,6 +46,9 @@ android_app {
"xz-java",
"androidx.leanback_leanback",
"androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-extensions",
],
lint: {
@@ -69,6 +72,9 @@ android_app {
static_libs: [
"xz-java",
"androidx.leanback_leanback",
+ "androidx.fragment_fragment",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-extensions",
],
aaptflags: ["--product tablet"],
@@ -94,6 +100,9 @@ android_app {
"xz-java",
"androidx.leanback_leanback",
"androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-extensions",
],
aaptflags: ["--product tv"],
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index a16f9f55b466..6e47689d585c 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -43,6 +43,19 @@
</intent-filter>
</receiver>
+ <receiver android:name="v2.model.TemporaryFileManager"
+ android:exported="false"
+ android:enabled="false">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
+ <activity android:name=".v2.ui.InstallLaunch"
+ android:configChanges="orientation|keyboardHidden|screenSize"
+ android:theme="@style/Theme.AlertDialogActivity"
+ android:exported="true"/>
+
<activity android:name=".InstallStart"
android:theme="@style/Theme.AlertDialogActivity"
android:exported="true"
@@ -81,6 +94,7 @@
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
+ android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
<activity android:name=".InstallInstalling"
@@ -95,6 +109,15 @@
</intent-filter>
</receiver>
+ <receiver android:name=".v2.model.InstallEventReceiver"
+ android:permission="android.permission.INSTALL_PACKAGES"
+ android:exported="false"
+ android:enabled="false">
+ <intent-filter android:priority="1">
+ <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
+ </intent-filter>
+ </receiver>
+
<activity android:name=".InstallSuccess"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index aa1fa164b021..811fa7380c48 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -32,8 +32,8 @@
<item name="android:windowNoTitle">true</item>
</style>
- <style name="Theme.AlertDialogActivity.NoDim">
- <item name="android:windowNoTitle">true</item>
+ <style name="Theme.AlertDialogActivity.NoDim"
+ parent="@style/Theme.AlertDialogActivity.NoActionBar">
<item name="android:backgroundDimAmount">0</item>
</style>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 74f04e093162..eef21991b845 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -36,12 +36,14 @@ import androidx.annotation.Nullable;
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
-public class InstallFailed extends AlertActivity {
+public class InstallFailed extends Activity {
private static final String LOG_TAG = InstallFailed.class.getSimpleName();
/** Label of the app that failed to install */
private CharSequence mLabel;
+ private AlertDialog mDialog;
+
/**
* Unhide the appropriate label for the statusCode.
*
@@ -53,19 +55,19 @@ public class InstallFailed extends AlertActivity {
View viewToEnable;
switch (statusCode) {
case PackageInstaller.STATUS_FAILURE_BLOCKED:
- viewToEnable = requireViewById(R.id.install_failed_blocked);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_blocked);
break;
case PackageInstaller.STATUS_FAILURE_CONFLICT:
- viewToEnable = requireViewById(R.id.install_failed_conflict);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_conflict);
break;
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
- viewToEnable = requireViewById(R.id.install_failed_incompatible);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_incompatible);
break;
case PackageInstaller.STATUS_FAILURE_INVALID:
- viewToEnable = requireViewById(R.id.install_failed_invalid_apk);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_invalid_apk);
break;
default:
- viewToEnable = requireViewById(R.id.install_failed);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed);
break;
}
@@ -105,12 +107,18 @@ public class InstallFailed extends AlertActivity {
// Store label for dialog
mLabel = as.label;
- mAlert.setIcon(as.icon);
- mAlert.setTitle(as.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.done),
- (ignored, ignored2) -> finish(), null);
- setupAlert();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(as.icon);
+ builder.setTitle(as.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.done),
+ (ignored, ignored2) -> finish());
+ builder.setOnCancelListener(dialog -> {
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
// Show out of space dialog if needed
if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 4992ef1e1c00..8d8254ad4058 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -19,6 +19,8 @@ package com.android.packageinstaller;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+import android.app.Activity;
+import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
@@ -43,7 +45,7 @@ import java.io.IOException;
* <p>This has two phases: First send the data to the package manager, then wait until the package
* manager processed the result.</p>
*/
-public class InstallInstalling extends AlertActivity {
+public class InstallInstalling extends Activity {
private static final String LOG_TAG = InstallInstalling.class.getSimpleName();
private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
@@ -67,6 +69,8 @@ public class InstallInstalling extends AlertActivity {
/** The button that can cancel this dialog */
private Button mCancelButton;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,10 +94,12 @@ public class InstallInstalling extends AlertActivity {
PackageUtil.AppSnippet as = getIntent()
.getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
- mAlert.setIcon(as.icon);
- mAlert.setTitle(as.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(as.icon);
+ builder.setTitle(as.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
@@ -106,9 +112,11 @@ public class InstallInstalling extends AlertActivity {
setResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.installing).setVisibility(View.VISIBLE);
+ });
+ builder.setCancelable(false);
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(R.id.installing).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
@@ -145,7 +153,7 @@ public class InstallInstalling extends AlertActivity {
}
}
- mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
+ mCancelButton = mDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index 483fb8c451ae..cf2f85ed5356 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -52,7 +52,7 @@ import java.io.OutputStream;
* If a package gets installed from a content URI this step stages the installation session
* reading bytes from the URI.
*/
-public class InstallStaging extends AlertActivity {
+public class InstallStaging extends Activity {
private static final String LOG_TAG = InstallStaging.class.getSimpleName();
private static final String STAGED_SESSION_ID = "STAGED_SESSION_ID";
@@ -65,6 +65,8 @@ public class InstallStaging extends AlertActivity {
/** The session the package is in */
private int mStagedSessionId;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,10 +74,13 @@ public class InstallStaging extends AlertActivity {
mInstaller = getPackageManager().getPackageInstaller();
setFinishOnTouchOutside(true);
- mAlert.setIcon(R.drawable.ic_file_download);
- mAlert.setTitle(getString(R.string.app_name_unknown));
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(R.drawable.ic_file_download);
+ builder.setTitle(getString(R.string.app_name_unknown));
+ builder.setView(R.layout.install_content_view);
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
if (mStagingTask != null) {
mStagingTask.cancel(true);
@@ -85,9 +90,21 @@ public class InstallStaging extends AlertActivity {
setResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.staging).setVisibility(View.VISIBLE);
+ });
+ builder.setOnCancelListener(dialog -> {
+ if (mStagingTask != null) {
+ mStagingTask.cancel(true);
+ }
+
+ cleanupStagingSession();
+
+ setResult(RESULT_CANCELED);
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(com.android.packageinstaller.R.id.staging)
+ .setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mStagedSessionId = savedInstanceState.getInt(STAGED_SESSION_ID, 0);
@@ -275,8 +292,9 @@ public class InstallStaging extends AlertActivity {
@Override
protected void onPreExecute() {
final long sizeBytes = getContentSizeBytes();
-
- mProgressBar = sizeBytes > 0 ? requireViewById(R.id.progress_indeterminate) : null;
+ if (sizeBytes > 0 && mDialog != null) {
+ mProgressBar = mDialog.requireViewById(R.id.progress_indeterminate);
+ }
if (mProgressBar != null) {
mProgressBar.setProgress(0);
mProgressBar.setMax(100);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 736e0efe872a..e2107ebe2525 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -40,7 +40,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
+import com.android.packageinstaller.v2.ui.InstallLaunch;
import java.util.Arrays;
/**
@@ -57,9 +57,23 @@ public class InstallStart extends Activity {
private final boolean mLocalLOGV = false;
+ // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
+ private static final boolean USE_PIA_V2 = false;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (USE_PIA_V2) {
+ Intent piaV2 = new Intent(getIntent());
+ piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getCallingPackage());
+ piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
+ piaV2.setClass(this, InstallLaunch.class);
+ piaV2.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(piaV2);
+ finish();
+ return;
+ }
mPackageManager = getPackageManager();
mUserManager = getSystemService(UserManager.class);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index fbc9525d4615..9af88c3b4694 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -34,7 +35,7 @@ import java.util.List;
/**
* Finish installation: Return status code to the caller or display "success" UI to user
*/
-public class InstallSuccess extends AlertActivity {
+public class InstallSuccess extends Activity {
private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
@Nullable
@@ -46,6 +47,8 @@ public class InstallSuccess extends AlertActivity {
@Nullable
private Intent mLaunchIntent;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -83,20 +86,27 @@ public class InstallSuccess extends AlertActivity {
return;
}
- mAlert.setIcon(mAppSnippet.icon);
- mAlert.setTitle(mAppSnippet.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
- null);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(mAppSnippet.icon);
+ builder.setTitle(mAppSnippet.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.launch), null);
+ builder.setNegativeButton(getString(R.string.done),
(ignored, ignored2) -> {
if (mAppPackageName != null) {
Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
}
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
+ });
+ builder.setOnCancelListener(dialog -> {
+ if (mAppPackageName != null) {
+ Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
+ }
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
// Enable or disable "launch" button
boolean enabled = false;
if (mLaunchIntent != null) {
@@ -107,7 +117,7 @@ public class InstallSuccess extends AlertActivity {
}
}
- Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
setResult(Activity.RESULT_OK, mLaunchIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index c5ae4a37b355..ceb580d170d0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -71,7 +71,7 @@ import java.util.List;
* Based on the user response the package is then installed by launching InstallAppConfirm
* sub activity. All state transitions are handled in this activity
*/
-public class PackageInstallerActivity extends AlertActivity {
+public class PackageInstallerActivity extends Activity {
private static final String TAG = "PackageInstaller";
private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
@@ -135,11 +135,13 @@ public class PackageInstallerActivity extends AlertActivity {
// Would the mOk button be enabled if this activity would be resumed
private boolean mEnableOk = false;
+ private AlertDialog mDialog;
+
private void startInstallConfirm() {
TextView viewToEnable;
if (mAppInfo != null) {
- viewToEnable = requireViewById(R.id.install_confirm_question_update);
+ viewToEnable = mDialog.requireViewById(R.id.install_confirm_question_update);
final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel();
final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
@@ -157,7 +159,7 @@ public class PackageInstallerActivity extends AlertActivity {
}
} else {
// This is a new application with no permissions.
- viewToEnable = requireViewById(R.id.install_confirm_question);
+ viewToEnable = mDialog.requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
@@ -480,10 +482,11 @@ public class PackageInstallerActivity extends AlertActivity {
}
private void bindUi() {
- mAlert.setIcon(mAppSnippet.icon);
- mAlert.setTitle(mAppSnippet.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(mAppSnippet.icon);
+ builder.setTitle(mAppSnippet.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
@@ -493,20 +496,26 @@ public class PackageInstallerActivity extends AlertActivity {
startInstall();
}
}
- }, null);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+ });
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setActivityResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
+ });
+ builder.setOnCancelListener(dialog -> {
+ // Cancel and finish
+ setActivityResult(RESULT_CANCELED);
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
- mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ mOk = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
if (!mOk.isInTouchMode()) {
- mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
+ mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/EventResultPersister.java
new file mode 100644
index 000000000000..4d2d911f7a8f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/EventResultPersister.java
@@ -0,0 +1,378 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.os.AsyncTask;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+/**
+ * Persists results of events and calls back observers when a matching result arrives.
+ */
+public class EventResultPersister {
+
+ /**
+ * Id passed to {@link #addObserver(int, EventResultObserver)} to generate new id
+ */
+ public static final int GENERATE_NEW_ID = Integer.MIN_VALUE;
+ /**
+ * The extra with the id to set in the intent delivered to
+ * {@link #onEventReceived(Context, Intent)}
+ */
+ public static final String EXTRA_ID = "EventResultPersister.EXTRA_ID";
+ public static final String EXTRA_SERVICE_ID = "EventResultPersister.EXTRA_SERVICE_ID";
+ private static final String TAG = EventResultPersister.class.getSimpleName();
+ /**
+ * Persisted state of this object
+ */
+ private final AtomicFile mResultsFile;
+
+ private final Object mLock = new Object();
+
+ /**
+ * Currently stored but not yet called back results (install id -> status, status message)
+ */
+ private final SparseArray<EventResult> mResults = new SparseArray<>();
+
+ /**
+ * Currently registered, not called back observers (install id -> observer)
+ */
+ private final SparseArray<EventResultObserver> mObservers = new SparseArray<>();
+
+ /**
+ * Always increasing counter for install event ids
+ */
+ private int mCounter;
+
+ /**
+ * If a write that will persist the state is scheduled
+ */
+ private boolean mIsPersistScheduled;
+
+ /**
+ * If the state was changed while the data was being persisted
+ */
+ private boolean mIsPersistingStateValid;
+
+ /**
+ * Read persisted state.
+ *
+ * @param resultFile The file the results are persisted in
+ */
+ EventResultPersister(@NonNull File resultFile) {
+ mResultsFile = new AtomicFile(resultFile);
+ mCounter = GENERATE_NEW_ID + 1;
+
+ try (FileInputStream stream = mResultsFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if ("results".equals(tagName)) {
+ mCounter = readIntAttribute(parser, "counter");
+ } else if ("result".equals(tagName)) {
+ int id = readIntAttribute(parser, "id");
+ int status = readIntAttribute(parser, "status");
+ int legacyStatus = readIntAttribute(parser, "legacyStatus");
+ String statusMessage = readStringAttribute(parser, "statusMessage");
+ int serviceId = readIntAttribute(parser, "serviceId");
+
+ if (mResults.get(id) != null) {
+ throw new Exception("id " + id + " has two results");
+ }
+
+ mResults.put(id, new EventResult(status, legacyStatus, statusMessage,
+ serviceId));
+ } else {
+ throw new Exception("unexpected tag");
+ }
+
+ nextElement(parser);
+ }
+ } catch (Exception e) {
+ mResults.clear();
+ writeState();
+ }
+ }
+
+ /**
+ * Progress parser to the next element.
+ *
+ * @param parser The parser to progress
+ */
+ private static void nextElement(@NonNull XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ do {
+ type = parser.next();
+ } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
+ }
+
+ /**
+ * Read an int attribute from the current element
+ *
+ * @param parser The parser to read from
+ * @param name The attribute name to read
+ * @return The value of the attribute
+ */
+ private static int readIntAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+ return Integer.parseInt(parser.getAttributeValue(null, name));
+ }
+
+ /**
+ * Read an String attribute from the current element
+ *
+ * @param parser The parser to read from
+ * @param name The attribute name to read
+ * @return The value of the attribute or null if the attribute is not set
+ */
+ private static String readStringAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+ return parser.getAttributeValue(null, name);
+ }
+
+ /**
+ * @return a new event id.
+ */
+ public int getNewId() throws OutOfIdsException {
+ synchronized (mLock) {
+ if (mCounter == Integer.MAX_VALUE) {
+ throw new OutOfIdsException();
+ }
+
+ mCounter++;
+ writeState();
+
+ return mCounter - 1;
+ }
+ }
+
+ /**
+ * Add a result. If the result is a pending user action, execute the pending user action
+ * directly and do not queue a result.
+ *
+ * @param context The context the event was received in
+ * @param intent The intent the activity received
+ */
+ void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
+ int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
+
+ if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+ Intent intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ intentToStart.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intentToStart);
+
+ return;
+ }
+
+ int id = intent.getIntExtra(EXTRA_ID, 0);
+ String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
+ int serviceId = intent.getIntExtra(EXTRA_SERVICE_ID, 0);
+
+ EventResultObserver observerToCall = null;
+ synchronized (mLock) {
+ int numObservers = mObservers.size();
+ for (int i = 0; i < numObservers; i++) {
+ if (mObservers.keyAt(i) == id) {
+ observerToCall = mObservers.valueAt(i);
+ mObservers.removeAt(i);
+
+ break;
+ }
+ }
+
+ if (observerToCall != null) {
+ observerToCall.onResult(status, legacyStatus, statusMessage, serviceId);
+ } else {
+ mResults.put(id, new EventResult(status, legacyStatus, statusMessage, serviceId));
+ writeState();
+ }
+ }
+ }
+
+ /**
+ * Persist current state. The persistence might be delayed.
+ */
+ private void writeState() {
+ synchronized (mLock) {
+ mIsPersistingStateValid = false;
+
+ if (!mIsPersistScheduled) {
+ mIsPersistScheduled = true;
+
+ AsyncTask.execute(() -> {
+ int counter;
+ SparseArray<EventResult> results;
+
+ while (true) {
+ // Take snapshot of state
+ synchronized (mLock) {
+ counter = mCounter;
+ results = mResults.clone();
+ mIsPersistingStateValid = true;
+ }
+
+ try (FileOutputStream stream = mResultsFile.startWrite()) {
+ try {
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startTag(null, "results");
+ serializer.attribute(null, "counter", Integer.toString(counter));
+
+ int numResults = results.size();
+ for (int i = 0; i < numResults; i++) {
+ serializer.startTag(null, "result");
+ serializer.attribute(null, "id",
+ Integer.toString(results.keyAt(i)));
+ serializer.attribute(null, "status",
+ Integer.toString(results.valueAt(i).status));
+ serializer.attribute(null, "legacyStatus",
+ Integer.toString(results.valueAt(i).legacyStatus));
+ if (results.valueAt(i).message != null) {
+ serializer.attribute(null, "statusMessage",
+ results.valueAt(i).message);
+ }
+ serializer.attribute(null, "serviceId",
+ Integer.toString(results.valueAt(i).serviceId));
+ serializer.endTag(null, "result");
+ }
+
+ serializer.endTag(null, "results");
+ serializer.endDocument();
+
+ mResultsFile.finishWrite(stream);
+ } catch (IOException e) {
+ Log.e(TAG, "error writing results", e);
+ mResultsFile.failWrite(stream);
+ mResultsFile.delete();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "error writing results", e);
+ mResultsFile.delete();
+ }
+
+ // Check if there was changed state since we persisted. If so, we need to
+ // persist again.
+ synchronized (mLock) {
+ if (mIsPersistingStateValid) {
+ mIsPersistScheduled = false;
+ break;
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Add an observer. If there is already an event for this id, call back inside of this call.
+ *
+ * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+ * @param observer The observer to call back.
+ * @return The id for this event
+ */
+ int addObserver(int id, @NonNull EventResultObserver observer)
+ throws OutOfIdsException {
+ synchronized (mLock) {
+ int resultIndex = -1;
+
+ if (id == GENERATE_NEW_ID) {
+ id = getNewId();
+ } else {
+ resultIndex = mResults.indexOfKey(id);
+ }
+
+ // Check if we can instantly call back
+ if (resultIndex >= 0) {
+ EventResult result = mResults.valueAt(resultIndex);
+
+ observer.onResult(result.status, result.legacyStatus, result.message,
+ result.serviceId);
+ mResults.removeAt(resultIndex);
+ writeState();
+ } else {
+ mObservers.put(id, observer);
+ }
+ }
+
+ return id;
+ }
+
+ /**
+ * Remove a observer.
+ *
+ * @param id The id the observer was added for
+ */
+ void removeObserver(int id) {
+ synchronized (mLock) {
+ mObservers.delete(id);
+ }
+ }
+
+ /**
+ * Call back when a result is received. Observer is removed when onResult it called.
+ */
+ public interface EventResultObserver {
+
+ void onResult(int status, int legacyStatus, @Nullable String message, int serviceId);
+ }
+
+ /**
+ * The status from an event.
+ */
+ private static class EventResult {
+
+ public final int status;
+ public final int legacyStatus;
+ @Nullable
+ public final String message;
+ public final int serviceId;
+
+ private EventResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
+ this.status = status;
+ this.legacyStatus = legacyStatus;
+ this.message = message;
+ this.serviceId = serviceId;
+ }
+ }
+
+ public static class OutOfIdsException extends Exception {
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallEventReceiver.java
new file mode 100644
index 000000000000..bcb11c884694
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallEventReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import androidx.annotation.NonNull;
+
+/**
+ * Receives install events and perists them using a {@link EventResultPersister}.
+ */
+public class InstallEventReceiver extends BroadcastReceiver {
+
+ private static final Object sLock = new Object();
+ private static EventResultPersister sReceiver;
+
+ /**
+ * Get the event receiver persisting the results
+ *
+ * @return The event receiver.
+ */
+ @NonNull
+ private static EventResultPersister getReceiver(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sReceiver == null) {
+ sReceiver = new EventResultPersister(
+ TemporaryFileManager.getInstallStateFile(context));
+ }
+ }
+
+ return sReceiver;
+ }
+
+ /**
+ * Add an observer. If there is already an event for this id, call back inside of this call.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+ * @param observer The observer to call back.
+ * @return The id for this event
+ */
+ static int addObserver(@NonNull Context context, int id,
+ @NonNull EventResultPersister.EventResultObserver observer)
+ throws EventResultPersister.OutOfIdsException {
+ return getReceiver(context).addObserver(id, observer);
+ }
+
+ /**
+ * Remove a observer.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer was added for
+ */
+ static void removeObserver(@NonNull Context context, int id) {
+ getReceiver(context).removeObserver(id);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ getReceiver(context).onEventReceived(context, intent);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
new file mode 100644
index 000000000000..7e7071f5dd24
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
@@ -0,0 +1,899 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import static com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery;
+import static com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo;
+import static com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet;
+import static com.android.packageinstaller.v2.model.PackageUtil.getPackageInfo;
+import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
+import static com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner;
+import static com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested;
+import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_DONE;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_INTERNAL_ERROR;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_POLICY;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.DLG_PACKAGE_ERROR;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.MutableLiveData;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.EventResultPersister.OutOfIdsException;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
+import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
+import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
+import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
+import com.android.packageinstaller.v2.model.installstagedata.InstallReady;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
+import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
+import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import java.io.File;
+import java.io.IOException;
+
+public class InstallRepository {
+
+ private static final String SCHEME_PACKAGE = "package";
+ private static final String BROADCAST_ACTION =
+ "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
+ private static final String TAG = InstallRepository.class.getSimpleName();
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final PackageInstaller mPackageInstaller;
+ private final UserManager mUserManager;
+ private final DevicePolicyManager mDevicePolicyManager;
+ private final AppOpsManager mAppOpsManager;
+ private final MutableLiveData<InstallStage> mStagingResult = new MutableLiveData<>();
+ private final MutableLiveData<InstallStage> mInstallResult = new MutableLiveData<>();
+ private final boolean mLocalLOGV = false;
+ private Intent mIntent;
+ private boolean mIsSessionInstall;
+ private boolean mIsTrustedSource;
+ /**
+ * Session ID for a session created when caller uses PackageInstaller APIs
+ */
+ private int mSessionId;
+ /**
+ * Session ID for a session created by this app
+ */
+ private int mStagedSessionId = SessionInfo.INVALID_ID;
+ private int mCallingUid;
+ private String mCallingPackage;
+ private SessionStager mSessionStager;
+ private AppOpRequestInfo mAppOpRequestInfo;
+ private AppSnippet mAppSnippet;
+ /**
+ * PackageInfo of the app being installed on device.
+ */
+ private PackageInfo mNewPackageInfo;
+
+ public InstallRepository(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mPackageInstaller = mPackageManager.getPackageInstaller();
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ mUserManager = context.getSystemService(UserManager.class);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ }
+
+ /**
+ * Extracts information from the incoming install intent, checks caller's permission to install
+ * packages, verifies that the caller is the install session owner (in case of a session based
+ * install) and checks if the current user has restrictions set that prevent app installation,
+ *
+ * @param intent the incoming {@link Intent} object for installing a package
+ * @param callerInfo {@link CallerInfo} that holds the callingUid and callingPackageName
+ * @return <p>{@link InstallAborted} if there are errors while performing the checks</p>
+ * <p>{@link InstallStaging} after successfully performing the checks</p>
+ */
+ public InstallStage performPreInstallChecks(Intent intent, CallerInfo callerInfo) {
+ mIntent = intent;
+
+ String callingAttributionTag = null;
+
+ mIsSessionInstall =
+ PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())
+ || PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
+
+ mSessionId = mIsSessionInstall
+ ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
+ : SessionInfo.INVALID_ID;
+
+ mCallingPackage = callerInfo.getPackageName();
+
+ if (mCallingPackage == null && mSessionId != SessionInfo.INVALID_ID) {
+ PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(mSessionId);
+ mCallingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
+ callingAttributionTag =
+ (sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null;
+ }
+
+ // Uid of the source package, coming from ActivityManager
+ mCallingUid = callerInfo.getUid();
+ if (mCallingUid == Process.INVALID_UID) {
+ Log.e(TAG, "Could not determine the launching uid.");
+ }
+ final ApplicationInfo sourceInfo = getSourceInfo(mCallingPackage);
+ // Uid of the source package, with a preference to uid from ApplicationInfo
+ final int originatingUid = sourceInfo != null ? sourceInfo.uid : mCallingUid;
+ mAppOpRequestInfo = new AppOpRequestInfo(
+ getPackageNameForUid(mContext, originatingUid, mCallingPackage),
+ originatingUid, callingAttributionTag);
+
+ if (mCallingUid == Process.INVALID_UID && sourceInfo == null) {
+ // Caller's identity could not be determined. Abort the install
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+
+ if (!isCallerSessionOwner(mPackageInstaller, originatingUid, mSessionId)) {
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+
+ mIsTrustedSource = isInstallRequestFromTrustedSource(sourceInfo, mIntent, originatingUid);
+
+ if (!isInstallPermissionGrantedOrRequested(mContext, mCallingUid, originatingUid,
+ mIsTrustedSource)) {
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+
+ String restriction = getDevicePolicyRestrictions();
+ if (restriction != null) {
+ InstallAborted.Builder abortedBuilder =
+ new InstallAborted.Builder(ABORT_REASON_POLICY).setMessage(restriction);
+ final Intent adminSupportDetailsIntent =
+ mDevicePolicyManager.createAdminSupportIntent(restriction);
+ if (adminSupportDetailsIntent != null) {
+ abortedBuilder.setResultIntent(adminSupportDetailsIntent);
+ }
+ return abortedBuilder.build();
+ }
+
+ maybeRemoveInvalidInstallerPackageName(callerInfo);
+
+ return new InstallStaging();
+ }
+
+ /**
+ * @return the ApplicationInfo for the installation source (the calling package), if available
+ */
+ @Nullable
+ private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
+ if (callingPackage == null) {
+ return null;
+ }
+ try {
+ return mPackageManager.getApplicationInfo(callingPackage, 0);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return null;
+ }
+ }
+
+ private boolean isInstallRequestFromTrustedSource(ApplicationInfo sourceInfo, Intent intent,
+ int originatingUid) {
+ boolean isNotUnknownSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
+ return sourceInfo != null && sourceInfo.isPrivilegedApp()
+ && (isNotUnknownSource
+ || isPermissionGranted(mContext, Manifest.permission.INSTALL_PACKAGES, originatingUid));
+ }
+
+ private String getDevicePolicyRestrictions() {
+ final String[] restrictions = new String[]{
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+ };
+
+ for (String restriction : restrictions) {
+ if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
+ continue;
+ }
+ return restriction;
+ }
+ return null;
+ }
+
+ private void maybeRemoveInvalidInstallerPackageName(CallerInfo callerInfo) {
+ final String installerPackageNameFromIntent =
+ mIntent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ if (installerPackageNameFromIntent == null) {
+ return;
+ }
+ if (!TextUtils.equals(installerPackageNameFromIntent, callerInfo.getPackageName())
+ && !isPermissionGranted(mPackageManager, Manifest.permission.INSTALL_PACKAGES,
+ callerInfo.getPackageName())) {
+ Log.e(TAG, "The given installer package name " + installerPackageNameFromIntent
+ + " is invalid. Remove it.");
+ EventLog.writeEvent(0x534e4554, "236687884", callerInfo.getUid(),
+ "Invalid EXTRA_INSTALLER_PACKAGE_NAME");
+ mIntent.removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ }
+ }
+
+ public void stageForInstall() {
+ Uri uri = mIntent.getData();
+ if (mIsSessionInstall || (uri != null && SCHEME_PACKAGE.equals(uri.getScheme()))) {
+ // For a session based install or installing with a package:// URI, there is no file
+ // for us to stage. Setting the mStagingResult as null will signal InstallViewModel to
+ // proceed with user confirmation stage.
+ mStagingResult.setValue(new InstallReady());
+ return;
+ }
+ if (uri != null
+ && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+ && canPackageQuery(mContext, mCallingUid, uri)) {
+
+ if (mStagedSessionId > 0) {
+ final PackageInstaller.SessionInfo info =
+ mPackageInstaller.getSessionInfo(mStagedSessionId);
+ if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) {
+ Log.w(TAG, "Session " + mStagedSessionId + " in funky state; ignoring");
+ if (info != null) {
+ cleanupStagingSession();
+ }
+ mStagedSessionId = 0;
+ }
+ }
+
+ // Session does not exist, or became invalid.
+ if (mStagedSessionId <= 0) {
+ // Create session here to be able to show error.
+ try (final AssetFileDescriptor afd =
+ mContext.getContentResolver().openAssetFileDescriptor(uri, "r")) {
+ ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null;
+ PackageInstaller.SessionParams params =
+ createSessionParams(mIntent, pfd, uri.toString());
+ mStagedSessionId = mPackageInstaller.createSession(params);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to create a staging session", e);
+ mStagingResult.setValue(
+ new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK))
+ .setActivityResultCode(Activity.RESULT_FIRST_USER)
+ .build());
+ return;
+ }
+ }
+
+ SessionStageListener listener = new SessionStageListener() {
+ @Override
+ public void onStagingSuccess(SessionInfo info) {
+ //TODO: Verify if the returned sessionInfo should be used anywhere
+ mStagingResult.setValue(new InstallReady());
+ }
+
+ @Override
+ public void onStagingFailure() {
+ cleanupStagingSession();
+ mStagingResult.setValue(
+ new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK))
+ .setActivityResultCode(Activity.RESULT_FIRST_USER)
+ .build());
+ }
+ };
+ if (mSessionStager != null) {
+ mSessionStager.cancel(true);
+ }
+ mSessionStager = new SessionStager(mContext, uri, mStagedSessionId, listener);
+ mSessionStager.execute();
+ }
+ }
+
+ private void cleanupStagingSession() {
+ if (mStagedSessionId > 0) {
+ try {
+ mPackageInstaller.abandonSession(mStagedSessionId);
+ } catch (SecurityException ignored) {
+ }
+ mStagedSessionId = 0;
+ }
+ }
+
+ private PackageInstaller.SessionParams createSessionParams(@NonNull Intent intent,
+ @Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER, Uri.class);
+ params.setPackageSource(
+ referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+ : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
+ params.setInstallAsInstantApp(false);
+ params.setReferrerUri(referrerUri);
+ params.setOriginatingUri(
+ intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI, Uri.class));
+ params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+ Process.INVALID_UID));
+ params.setInstallerPackageName(intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME));
+ params.setInstallReason(PackageManager.INSTALL_REASON_USER);
+ // Disable full screen intent usage by for sideloads.
+ params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT,
+ PackageInstaller.SessionParams.PERMISSION_STATE_DENIED);
+
+ if (pfd != null) {
+ try {
+ final PackageInstaller.InstallInfo result = mPackageInstaller.readInstallInfo(pfd,
+ debugPathName, 0);
+ params.setAppPackageName(result.getPackageName());
+ params.setInstallLocation(result.getInstallLocation());
+ params.setSize(result.calculateInstalledSize(params, pfd));
+ } catch (PackageInstaller.PackageParsingException e) {
+ Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e);
+ params.setSize(pfd.getStatSize());
+ } catch (IOException e) {
+ Log.e(TAG,
+ "Cannot calculate installed size " + debugPathName
+ + ". Try only apk size.", e);
+ }
+ } else {
+ Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.");
+ }
+ return params;
+ }
+
+ /**
+ * Processes Install session, file:// or package:// URI to generate data pertaining to user
+ * confirmation for an install. This method also checks if the source app has the AppOp granted
+ * to install unknown apps. If an AppOp is to be requested, cache the user action prompt data to
+ * be reused once appOp has been granted
+ *
+ * @return <ul>
+ * <li>InstallAborted </li>
+ * <ul>
+ * <li> If install session is invalid (not sealed or resolvedBaseApk path
+ * is invalid) </li>
+ * <li> Source app doesn't have visibility to target app </li>
+ * <li> The APK is invalid </li>
+ * <li> URI is invalid </li>
+ * <li> Can't get ApplicationInfo for source app, to request AppOp </li>
+ * </ul>
+ * <li> InstallUserActionRequired</li>
+ * <ul>
+ * <li> If AppOP is granted and user action is required to proceed
+ * with install </li>
+ * <li> If AppOp grant is to be requested from the user</li>
+ * </ul>
+ * </ul>
+ */
+ public InstallStage requestUserConfirmation() {
+ if (mIsTrustedSource) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "install allowed");
+ }
+ // Returns InstallUserActionRequired stage if install details could be successfully
+ // computed, else it returns InstallAborted.
+ return generateConfirmationSnippet();
+ } else {
+ InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
+ if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
+ // Source app already has appOp granted.
+ return generateConfirmationSnippet();
+ } else {
+ return unknownSourceStage;
+ }
+ }
+ }
+
+
+ private InstallStage generateConfirmationSnippet() {
+ final Object packageSource;
+ int pendingUserActionReason = -1;
+ if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(mIntent.getAction())) {
+ final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
+ String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
+
+ if (info == null || !info.isSealed() || resolvedPath == null) {
+ Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ packageSource = Uri.fromFile(new File(resolvedPath));
+ // TODO: Not sure where is this used yet. PIA.java passes it to
+ // InstallInstalling if not null
+ // mOriginatingURI = null;
+ // mReferrerURI = null;
+ pendingUserActionReason = info.getPendingUserActionReason();
+ } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(mIntent.getAction())) {
+ final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
+
+ if (info == null || !info.isPreApprovalRequested()) {
+ Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ packageSource = info;
+ // mOriginatingURI = null;
+ // mReferrerURI = null;
+ pendingUserActionReason = info.getPendingUserActionReason();
+ } else {
+ // Two possible origins:
+ // 1. Installation with SCHEME_PACKAGE.
+ // 2. Installation with "file://" for session created by this app
+ if (mIntent.getData() != null && mIntent.getData().getScheme().equals(SCHEME_PACKAGE)) {
+ packageSource = mIntent.getData();
+ } else {
+ SessionInfo stagedSessionInfo = mPackageInstaller.getSessionInfo(mStagedSessionId);
+ packageSource = Uri.fromFile(new File(stagedSessionInfo.getResolvedBaseApkPath()));
+ }
+ // mOriginatingURI = mIntent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+ // mReferrerURI = mIntent.getParcelableExtra(Intent.EXTRA_REFERRER);
+ pendingUserActionReason = PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE;
+ }
+
+ // if there's nothing to do, quietly slip into the ether
+ if (packageSource == null) {
+ Log.w(TAG, "Unspecified source");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_URI))
+ .setActivityResultCode(Activity.RESULT_FIRST_USER)
+ .build();
+ }
+
+ return processAppSnippet(packageSource, pendingUserActionReason);
+ }
+
+ /**
+ * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
+ * session) to set up the installer for this install.
+ *
+ * @param source The source of package URI or SessionInfo
+ * @return {@code true} iff the installer could be set up
+ */
+ private InstallStage processAppSnippet(Object source, int userActionReason) {
+ if (source instanceof Uri) {
+ return processPackageUri((Uri) source, userActionReason);
+ } else if (source instanceof SessionInfo) {
+ return processSessionInfo((SessionInfo) source, userActionReason);
+ }
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+
+ /**
+ * Parse the Uri and set up the installer for this package.
+ *
+ * @param packageUri The URI to parse
+ * @return {@code true} iff the installer could be set up
+ */
+ private InstallStage processPackageUri(final Uri packageUri, int userActionReason) {
+ final String scheme = packageUri.getScheme();
+ final String packageName = packageUri.getSchemeSpecificPart();
+
+ if (scheme == null) {
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+
+ if (mLocalLOGV) {
+ Log.i(TAG, "processPackageUri(): uri = " + packageUri + ", scheme = " + scheme);
+ }
+
+ switch (scheme) {
+ case SCHEME_PACKAGE -> {
+ for (UserHandle handle : mUserManager.getUserHandles(true)) {
+ PackageManager pmForUser = mContext.createContextAsUser(handle, 0)
+ .getPackageManager();
+ try {
+ if (pmForUser.canPackageQuery(mCallingPackage, packageName)) {
+ mNewPackageInfo = pmForUser.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ }
+ } catch (NameNotFoundException ignored) {
+ }
+ }
+ if (mNewPackageInfo == null) {
+ Log.w(TAG, "Requested package " + packageUri.getSchemeSpecificPart()
+ + " not available. Discontinuing installation");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .setErrorDialogType(DLG_PACKAGE_ERROR)
+ .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK))
+ .setActivityResultCode(Activity.RESULT_FIRST_USER)
+ .build();
+ }
+ mAppSnippet = getAppSnippet(mContext, mNewPackageInfo);
+ if (mLocalLOGV) {
+ Log.i(TAG, "Created snippet for " + mAppSnippet.getLabel());
+ }
+ }
+ case ContentResolver.SCHEME_FILE -> {
+ File sourceFile = new File(packageUri.getPath());
+ mNewPackageInfo = getPackageInfo(mContext, sourceFile,
+ PackageManager.GET_PERMISSIONS);
+
+ // Check for parse errors
+ if (mNewPackageInfo == null) {
+ Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .setErrorDialogType(DLG_PACKAGE_ERROR)
+ .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK))
+ .setActivityResultCode(Activity.RESULT_FIRST_USER)
+ .build();
+ }
+ if (mLocalLOGV) {
+ Log.i(TAG, "Creating snippet for local file " + sourceFile);
+ }
+ mAppSnippet = getAppSnippet(mContext, mNewPackageInfo.applicationInfo, sourceFile);
+ }
+ default -> {
+ Log.e(TAG, "Unexpected URI scheme " + packageUri);
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ }
+
+ return new InstallUserActionRequired.Builder(
+ USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
+ .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
+ .setAppUpdating(isAppUpdating(mNewPackageInfo))
+ .build();
+ }
+
+ /**
+ * Use the SessionInfo and set up the installer for pre-commit install session.
+ *
+ * @param sessionInfo The SessionInfo to compose
+ * @return {@code true} iff the installer could be set up
+ */
+ private InstallStage processSessionInfo(@NonNull SessionInfo sessionInfo,
+ int userActionReason) {
+ mNewPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName());
+
+ mAppSnippet = getAppSnippet(mContext, sessionInfo);
+ return new InstallUserActionRequired.Builder(
+ USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
+ .setAppUpdating(isAppUpdating(mNewPackageInfo))
+ .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
+ .build();
+ }
+
+ private String getUpdateMessage(PackageInfo pkgInfo, int userActionReason) {
+ if (isAppUpdating(pkgInfo)) {
+ final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo);
+ final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
+
+ if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
+ && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
+ return mContext.getString(R.string.install_confirm_question_update_owner_reminder,
+ requestedUpdateOwnerLabel, existingUpdateOwnerLabel);
+ }
+ }
+ return null;
+ }
+
+ private CharSequence getExistingUpdateOwnerLabel(PackageInfo pkgInfo) {
+ try {
+ final String packageName = pkgInfo.packageName;
+ final InstallSourceInfo sourceInfo = mPackageManager.getInstallSourceInfo(packageName);
+ final String existingUpdateOwner = sourceInfo.getUpdateOwnerPackageName();
+ return getApplicationLabel(existingUpdateOwner);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ private CharSequence getApplicationLabel(String packageName) {
+ try {
+ final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
+ ApplicationInfoFlags.of(0));
+ return mPackageManager.getApplicationLabel(appInfo);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ private boolean isAppUpdating(PackageInfo newPkgInfo) {
+ String pkgName = newPkgInfo.packageName;
+ // Check if there is already a package on the device with this name
+ // but it has been renamed to something else.
+ String[] oldName = mPackageManager.canonicalToCurrentPackageNames(new String[]{pkgName});
+ if (oldName != null && oldName.length > 0 && oldName[0] != null) {
+ pkgName = oldName[0];
+ newPkgInfo.packageName = pkgName;
+ newPkgInfo.applicationInfo.packageName = pkgName;
+ }
+ // Check if package is already installed. display confirmation dialog if replacing pkg
+ try {
+ // This is a little convoluted because we want to get all uninstalled
+ // apps, but this may include apps with just data, and if it is just
+ // data we still want to count it as "installed".
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(pkgName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ return false;
+ }
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Once the user returns from Settings related to installing from unknown sources, reattempt
+ * the installation if the source app is granted permission to install other apps. Abort the
+ * installation if the source app is still not granted installing permission.
+ * @return {@link InstallUserActionRequired} containing data required to ask user confirmation
+ * to proceed with the install.
+ * {@link InstallAborted} if there was an error while recomputing, or the source still
+ * doesn't have install permission.
+ */
+ public InstallStage reattemptInstall() {
+ InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
+ if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
+ // Source app now has appOp granted.
+ return generateConfirmationSnippet();
+ } else if (unknownSourceStage.getStageCode() == InstallStage.STAGE_ABORTED) {
+ // There was some error in determining the AppOp code for the source app.
+ // Abort installation
+ return unknownSourceStage;
+ } else {
+ // AppOpsManager again returned a MODE_ERRORED or MODE_DEFAULT op code. This was
+ // unexpected while reattempting the install. Let's abort it.
+ Log.e(TAG, "AppOp still not granted.");
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ }
+
+ private InstallStage handleUnknownSources(AppOpRequestInfo requestInfo) {
+ if (requestInfo.getCallingPackage() == null) {
+ Log.i(TAG, "No source found for package " + mNewPackageInfo.packageName);
+ return new InstallUserActionRequired.Builder(
+ USER_ACTION_REASON_ANONYMOUS_SOURCE, null)
+ .build();
+ }
+ // Shouldn't use static constant directly, see b/65534401.
+ final String appOpStr =
+ AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpStr,
+ requestInfo.getOriginatingUid(),
+ requestInfo.getCallingPackage(), requestInfo.getAttributionTag(),
+ "Started package installation activity");
+
+ if (mLocalLOGV) {
+ Log.i(TAG, "handleUnknownSources(): appMode=" + appOpMode);
+ }
+ switch (appOpMode) {
+ case AppOpsManager.MODE_DEFAULT:
+ mAppOpsManager.setMode(appOpStr, requestInfo.getOriginatingUid(),
+ requestInfo.getCallingPackage(), AppOpsManager.MODE_ERRORED);
+ // fall through
+ case AppOpsManager.MODE_ERRORED:
+ try {
+ ApplicationInfo sourceInfo =
+ mPackageManager.getApplicationInfo(requestInfo.getCallingPackage(), 0);
+ AppSnippet sourceAppSnippet = getAppSnippet(mContext, sourceInfo);
+ return new InstallUserActionRequired.Builder(
+ USER_ACTION_REASON_UNKNOWN_SOURCE, sourceAppSnippet)
+ .setDialogMessage(requestInfo.getCallingPackage())
+ .build();
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Did not find appInfo for " + requestInfo.getCallingPackage());
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ case AppOpsManager.MODE_ALLOWED:
+ return new InstallReady();
+ default:
+ Log.e(TAG, "Invalid app op mode " + appOpMode
+ + " for OP_REQUEST_INSTALL_PACKAGES found for uid "
+ + requestInfo.getOriginatingUid());
+ return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
+ }
+ }
+
+
+ /**
+ * Kick off the installation. Register a broadcast listener to get the result of the
+ * installation and commit the staged session here. If the installation was session based,
+ * signal the PackageInstaller that the user has granted permission to proceed with the install
+ */
+ public void initiateInstall() {
+ if (mSessionId > 0) {
+ mPackageInstaller.setPermissionsResult(mSessionId, true);
+ mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_DONE)
+ .setActivityResultCode(Activity.RESULT_OK).build());
+ return;
+ }
+
+ Uri uri = mIntent.getData();
+ if (uri != null && SCHEME_PACKAGE.equals(uri.getScheme())) {
+ try {
+ mPackageManager.installExistingPackage(mNewPackageInfo.packageName);
+ setStageBasedOnResult(PackageInstaller.STATUS_SUCCESS, -1, null, -1);
+ } catch (PackageManager.NameNotFoundException e) {
+ setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
+ }
+ return;
+ }
+
+ if (mStagedSessionId <= 0) {
+ // How did we even land here?
+ Log.e(TAG, "Invalid local session and caller initiated session");
+ mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
+ .build());
+ return;
+ }
+
+ int installId;
+ try {
+ mInstallResult.setValue(new InstallInstalling(mAppSnippet));
+ installId = InstallEventReceiver.addObserver(mContext,
+ EventResultPersister.GENERATE_NEW_ID, this::setStageBasedOnResult);
+ } catch (OutOfIdsException e) {
+ setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
+ return;
+ }
+
+ Intent broadcastIntent = new Intent(BROADCAST_ACTION);
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ broadcastIntent.setPackage(mContext.getPackageName());
+ broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, installId);
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, installId, broadcastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
+
+ try {
+ PackageInstaller.Session session = mPackageInstaller.openSession(mStagedSessionId);
+ session.commit(pendingIntent.getIntentSender());
+ } catch (Exception e) {
+ Log.e(TAG, "Session " + mStagedSessionId + " could not be opened.", e);
+ mPackageInstaller.abandonSession(mStagedSessionId);
+ setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
+ }
+ }
+
+ private void setStageBasedOnResult(int statusCode, int legacyStatus, String message,
+ int serviceId) {
+ if (statusCode == PackageInstaller.STATUS_SUCCESS) {
+ boolean shouldReturnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+
+ InstallSuccess.Builder successBuilder = new InstallSuccess.Builder(mAppSnippet)
+ .setShouldReturnResult(shouldReturnResult);
+ Intent resultIntent;
+ if (shouldReturnResult) {
+ resultIntent = new Intent()
+ .putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED);
+ } else {
+ resultIntent = mPackageManager
+ .getLaunchIntentForPackage(mNewPackageInfo.packageName);
+ }
+ successBuilder.setResultIntent(resultIntent);
+
+ mInstallResult.setValue(successBuilder.build());
+ } else {
+ mInstallResult.setValue(
+ new InstallFailed(mAppSnippet, statusCode, legacyStatus, message));
+ }
+ }
+
+ public MutableLiveData<InstallStage> getInstallResult() {
+ return mInstallResult;
+ }
+
+ /**
+ * Cleanup the staged session. Also signal the packageinstaller that an install session is to
+ * be aborted
+ */
+ public void cleanupInstall() {
+ if (mSessionId > 0) {
+ mPackageInstaller.setPermissionsResult(mSessionId, false);
+ } else if (mStagedSessionId > 0) {
+ cleanupStagingSession();
+ }
+ }
+
+ /**
+ * When the identity of the install source could not be determined, user can skip checking the
+ * source and directly proceed with the install.
+ */
+ public InstallStage forcedSkipSourceCheck() {
+ return generateConfirmationSnippet();
+ }
+
+ public MutableLiveData<Integer> getStagingProgress() {
+ if (mSessionStager != null) {
+ return mSessionStager.getProgress();
+ }
+ return new MutableLiveData<>(0);
+ }
+
+ public MutableLiveData<InstallStage> getStagingResult() {
+ return mStagingResult;
+ }
+
+ public interface SessionStageListener {
+
+ void onStagingSuccess(SessionInfo info);
+
+ void onStagingFailure();
+ }
+
+ public static class CallerInfo {
+
+ private final String mPackageName;
+ private final int mUid;
+
+ public CallerInfo(String packageName, int uid) {
+ mPackageName = packageName;
+ mUid = uid;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+ }
+
+ public static class AppOpRequestInfo {
+
+ private String mCallingPackage;
+ private String mAttributionTag;
+ private int mOrginatingUid;
+
+ public AppOpRequestInfo(String callingPackage, int orginatingUid, String attributionTag) {
+ mCallingPackage = callingPackage;
+ mOrginatingUid = orginatingUid;
+ mAttributionTag = attributionTag;
+ }
+
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ public String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ public int getOriginatingUid() {
+ return mOrginatingUid;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
new file mode 100644
index 000000000000..9c15fd50f216
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
@@ -0,0 +1,445 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Process;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class PackageUtil {
+
+ private static final String TAG = InstallRepository.class.getSimpleName();
+ private static final String DOWNLOADS_AUTHORITY = "downloads";
+ private static final String SPLIT_BASE_APK_END_WITH = "base.apk";
+
+ /**
+ * Determines if the UID belongs to the system downloads provider and returns the
+ * {@link ApplicationInfo} of the provider
+ *
+ * @param uid UID of the caller
+ * @return {@link ApplicationInfo} of the provider if a downloads provider exists, it is a
+ * system app, and its UID matches with the passed UID, null otherwise.
+ */
+ public static ApplicationInfo getSystemDownloadsProviderInfo(PackageManager pm, int uid) {
+ final ProviderInfo providerInfo = pm.resolveContentProvider(
+ DOWNLOADS_AUTHORITY, 0);
+ if (providerInfo == null) {
+ // There seems to be no currently enabled downloads provider on the system.
+ return null;
+ }
+ ApplicationInfo appInfo = providerInfo.applicationInfo;
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && uid == appInfo.uid) {
+ return appInfo;
+ }
+ return null;
+ }
+
+ /**
+ * Get the maximum target sdk for a UID.
+ *
+ * @param context The context to use
+ * @param uid The UID requesting the install/uninstall
+ * @return The maximum target SDK or -1 if the uid does not match any packages.
+ */
+ public static int getMaxTargetSdkVersionForUid(@NonNull Context context, int uid) {
+ PackageManager pm = context.getPackageManager();
+ final String[] packages = pm.getPackagesForUid(uid);
+ int targetSdkVersion = -1;
+ if (packages != null) {
+ for (String packageName : packages) {
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
+ }
+ }
+ }
+ return targetSdkVersion;
+ }
+
+ public static boolean canPackageQuery(Context context, int callingUid, Uri packageUri) {
+ PackageManager pm = context.getPackageManager();
+ ProviderInfo info = pm.resolveContentProvider(packageUri.getAuthority(),
+ PackageManager.ComponentInfoFlags.of(0));
+ if (info == null) {
+ return false;
+ }
+ String targetPackage = info.packageName;
+
+ String[] callingPackages = pm.getPackagesForUid(callingUid);
+ if (callingPackages == null) {
+ return false;
+ }
+ for (String callingPackage : callingPackages) {
+ try {
+ if (pm.canPackageQuery(callingPackage, targetPackage)) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // no-op
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param context the {@link Context} object
+ * @param permission the permission name to check
+ * @param callingUid the UID of the caller who's permission is being checked
+ * @return {@code true} if the callingUid is granted the said permission
+ */
+ public static boolean isPermissionGranted(Context context, String permission, int callingUid) {
+ return context.checkPermission(permission, -1, callingUid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * @param pm the {@link PackageManager} object
+ * @param permission the permission name to check
+ * @param packageName the name of the package who's permission is being checked
+ * @return {@code true} if the package is granted the said permission
+ */
+ public static boolean isPermissionGranted(PackageManager pm, String permission,
+ String packageName) {
+ return pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * @param context the {@link Context} object
+ * @param callingUid the UID of the caller who's permission is being checked
+ * @param originatingUid the UID from where install is being originated. This could be same as
+ * callingUid or it will be the UID of the package performing a session based install
+ * @param isTrustedSource whether install request is coming from a privileged app or an app that
+ * has {@link Manifest.permission.INSTALL_PACKAGES} permission granted
+ * @return {@code true} if the package is granted the said permission
+ */
+ public static boolean isInstallPermissionGrantedOrRequested(Context context, int callingUid,
+ int originatingUid, boolean isTrustedSource) {
+ boolean isDocumentsManager =
+ isPermissionGranted(context, Manifest.permission.MANAGE_DOCUMENTS, callingUid);
+ boolean isSystemDownloadsProvider =
+ getSystemDownloadsProviderInfo(context.getPackageManager(), callingUid) != null;
+
+ if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager) {
+
+ final int targetSdkVersion = getMaxTargetSdkVersionForUid(context, originatingUid);
+ if (targetSdkVersion < 0) {
+ // Invalid originating uid supplied. Abort install.
+ Log.w(TAG, "Cannot get target sdk version for uid " + originatingUid);
+ return false;
+ } else if (targetSdkVersion >= Build.VERSION_CODES.O
+ && !isUidRequestingPermission(context.getPackageManager(), originatingUid,
+ Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
+ Log.e(TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ + Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param pm the {@link PackageManager} object
+ * @param uid the UID of the caller who's permission is being checked
+ * @param permission the permission name to check
+ * @return {@code true} if the caller is requesting the said permission in its Manifest
+ */
+ public static boolean isUidRequestingPermission(PackageManager pm, int uid, String permission) {
+ final String[] packageNames = pm.getPackagesForUid(uid);
+ if (packageNames == null) {
+ return false;
+ }
+ for (final String packageName : packageNames) {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
+ continue;
+ }
+ if (packageInfo.requestedPermissions != null
+ && Arrays.asList(packageInfo.requestedPermissions).contains(permission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param pi the {@link PackageInstaller} object to use
+ * @param originatingUid the UID of the package performing a session based install
+ * @param sessionId ID of the install session
+ * @return {@code true} if the caller is the session owner
+ */
+ public static boolean isCallerSessionOwner(PackageInstaller pi, int originatingUid,
+ int sessionId) {
+ if (sessionId == SessionInfo.INVALID_ID) {
+ return false;
+ }
+ if (originatingUid == Process.ROOT_UID) {
+ return true;
+ }
+ PackageInstaller.SessionInfo sessionInfo = pi.getSessionInfo(sessionId);
+ if (sessionInfo == null) {
+ return false;
+ }
+ int installerUid = sessionInfo.getInstallerUid();
+ return originatingUid == installerUid;
+ }
+
+ /**
+ * Generates a stub {@link PackageInfo} object for the given packageName
+ */
+ public static PackageInfo generateStubPackageInfo(String packageName) {
+ final PackageInfo info = new PackageInfo();
+ final ApplicationInfo aInfo = new ApplicationInfo();
+ info.applicationInfo = aInfo;
+ info.packageName = info.applicationInfo.packageName = packageName;
+ return info;
+ }
+
+ /**
+ * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
+ * {@link SessionInfo} object
+ */
+ public static AppSnippet getAppSnippet(Context context, SessionInfo info) {
+ PackageManager pm = context.getPackageManager();
+ CharSequence label = info.getAppLabel();
+ Drawable icon = info.getAppIcon() != null ?
+ new BitmapDrawable(context.getResources(), info.getAppIcon())
+ : pm.getDefaultActivityIcon();
+ return new AppSnippet(label, icon);
+ }
+
+ /**
+ * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
+ * {@link PackageInfo} object
+ */
+ public static AppSnippet getAppSnippet(Context context, PackageInfo pkgInfo) {
+ return getAppSnippet(context, pkgInfo.applicationInfo);
+ }
+
+ /**
+ * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
+ * {@link ApplicationInfo} object
+ */
+ public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo) {
+ PackageManager pm = context.getPackageManager();
+ CharSequence label = pm.getApplicationLabel(appInfo);
+ Drawable icon = pm.getApplicationIcon(appInfo);
+ return new AppSnippet(label, icon);
+ }
+
+ /**
+ * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
+ * supplied APK file
+ */
+ public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo,
+ File sourceFile) {
+ ApplicationInfo appInfoFromFile = processAppInfoForFile(appInfo, sourceFile);
+ CharSequence label = getAppLabelFromFile(context, appInfoFromFile);
+ Drawable icon = getAppIconFromFile(context, appInfoFromFile);
+ return new AppSnippet(label, icon);
+ }
+
+ /**
+ * Utility method to load application label
+ *
+ * @param context context of package that can load the resources
+ * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+ */
+ public static CharSequence getAppLabelFromFile(Context context, ApplicationInfo appInfo) {
+ PackageManager pm = context.getPackageManager();
+ CharSequence label = null;
+ // Try to load the label from the package's resources. If an app has not explicitly
+ // specified any label, just use the package name.
+ if (appInfo.labelRes != 0) {
+ try {
+ label = appInfo.loadLabel(pm);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ if (label == null) {
+ label = (appInfo.nonLocalizedLabel != null) ?
+ appInfo.nonLocalizedLabel : appInfo.packageName;
+ }
+ return label;
+ }
+
+ /**
+ * Utility method to load application icon
+ *
+ * @param context context of package that can load the resources
+ * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+ */
+ public static Drawable getAppIconFromFile(Context context, ApplicationInfo appInfo) {
+ PackageManager pm = context.getPackageManager();
+ Drawable icon = null;
+ // Try to load the icon from the package's resources. If an app has not explicitly
+ // specified any resource, just use the default icon for now.
+ try {
+ if (appInfo.icon != 0) {
+ try {
+ icon = appInfo.loadIcon(pm);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ if (icon == null) {
+ icon = context.getPackageManager().getDefaultActivityIcon();
+ }
+ } catch (OutOfMemoryError e) {
+ Log.i(TAG, "Could not load app icon", e);
+ }
+ return icon;
+ }
+
+ private static ApplicationInfo processAppInfoForFile(ApplicationInfo appInfo, File sourceFile) {
+ final String archiveFilePath = sourceFile.getAbsolutePath();
+ appInfo.publicSourceDir = archiveFilePath;
+
+ if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
+ final File[] files = sourceFile.getParentFile().listFiles();
+ final String[] splits = Arrays.stream(appInfo.splitNames)
+ .map(i -> findFilePath(files, i + ".apk"))
+ .filter(Objects::nonNull)
+ .toArray(String[]::new);
+
+ appInfo.splitSourceDirs = splits;
+ appInfo.splitPublicSourceDirs = splits;
+ }
+ return appInfo;
+ }
+
+ private static String findFilePath(File[] files, String postfix) {
+ for (File file : files) {
+ final String path = file.getAbsolutePath();
+ if (path.endsWith(postfix)) {
+ return path;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the packageName corresponding to a UID.
+ */
+ public static String getPackageNameForUid(Context context, int sourceUid,
+ String callingPackage) {
+ if (sourceUid == Process.INVALID_UID) {
+ return null;
+ }
+ // If the sourceUid belongs to the system downloads provider, we explicitly return the
+ // name of the Download Manager package. This is because its UID is shared with multiple
+ // packages, resulting in uncertainty about which package will end up first in the list
+ // of packages associated with this UID
+ PackageManager pm = context.getPackageManager();
+ ApplicationInfo systemDownloadProviderInfo = getSystemDownloadsProviderInfo(
+ pm, sourceUid);
+ if (systemDownloadProviderInfo != null) {
+ return systemDownloadProviderInfo.packageName;
+ }
+ String[] packagesForUid = pm.getPackagesForUid(sourceUid);
+ if (packagesForUid == null) {
+ return null;
+ }
+ if (packagesForUid.length > 1) {
+ if (callingPackage != null) {
+ for (String packageName : packagesForUid) {
+ if (packageName.equals(callingPackage)) {
+ return packageName;
+ }
+ }
+ }
+ Log.i(TAG, "Multiple packages found for source uid " + sourceUid);
+ }
+ return packagesForUid[0];
+ }
+
+ /**
+ * Utility method to get package information for a given {@link File}
+ */
+ public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
+ String filePath = sourceFile.getAbsolutePath();
+ if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
+ File dir = sourceFile.getParentFile();
+ if (dir.listFiles().length > 1) {
+ // split apks, use file directory to get archive info
+ filePath = dir.getPath();
+ }
+ }
+ try {
+ return context.getPackageManager().getPackageArchiveInfo(filePath, flags);
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
+
+ /**
+ * The class to hold an incoming package's icon and label.
+ * See {@link #getAppSnippet(Context, SessionInfo)},
+ * {@link #getAppSnippet(Context, PackageInfo)},
+ * {@link #getAppSnippet(Context, ApplicationInfo)},
+ * {@link #getAppSnippet(Context, ApplicationInfo, File)}
+ */
+ public static class AppSnippet {
+
+ private CharSequence mLabel;
+ private Drawable mIcon;
+
+ public AppSnippet(CharSequence label, Drawable icon) {
+ mLabel = label;
+ mIcon = icon;
+ }
+
+ public AppSnippet() {
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(CharSequence mLabel) {
+ this.mLabel = mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public void setIcon(Drawable mIcon) {
+ this.mIcon = mIcon;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
new file mode 100644
index 000000000000..a2c81f11cf68
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
@@ -0,0 +1,126 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
+
+import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+import androidx.lifecycle.MutableLiveData;
+import com.android.packageinstaller.v2.model.InstallRepository.SessionStageListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class SessionStager extends AsyncTask<Void, Integer, SessionInfo> {
+
+ private static final String TAG = SessionStager.class.getSimpleName();
+ private final Context mContext;
+ private final Uri mUri;
+ private final int mStagedSessionId;
+ private final MutableLiveData<Integer> mProgressLiveData = new MutableLiveData<>(0);
+ private final SessionStageListener mListener;
+
+ SessionStager(Context context, Uri uri, int stagedSessionId, SessionStageListener listener) {
+ mContext = context;
+ mUri = uri;
+ mStagedSessionId = stagedSessionId;
+ mListener = listener;
+ }
+
+ @Override
+ protected PackageInstaller.SessionInfo doInBackground(Void... params) {
+ PackageInstaller pi = mContext.getPackageManager().getPackageInstaller();
+ try (PackageInstaller.Session session = pi.openSession(mStagedSessionId);
+ InputStream in = mContext.getContentResolver().openInputStream(mUri)) {
+ session.setStagingProgress(0);
+
+ if (in == null) {
+ return null;
+ }
+ final long sizeBytes = getContentSizeBytes();
+ mProgressLiveData.postValue(sizeBytes > 0 ? 0 : -1);
+
+ long totalRead = 0;
+ try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
+ byte[] buffer = new byte[1024 * 1024];
+ while (true) {
+ int numRead = in.read(buffer);
+
+ if (numRead == -1) {
+ session.fsync(out);
+ break;
+ }
+
+ if (isCancelled()) {
+ break;
+ }
+
+ out.write(buffer, 0, numRead);
+ if (sizeBytes > 0) {
+ totalRead += numRead;
+ float fraction = ((float) totalRead / (float) sizeBytes);
+ session.setStagingProgress(fraction);
+ publishProgress((int) (fraction * 100.0));
+ }
+ }
+ }
+ return pi.getSessionInfo(mStagedSessionId);
+ } catch (IOException | SecurityException | IllegalStateException
+ | IllegalArgumentException e) {
+ Log.w(TAG, "Error staging apk from content URI", e);
+ return null;
+ }
+ }
+
+ private long getContentSizeBytes() {
+ try (AssetFileDescriptor afd = mContext.getContentResolver()
+ .openAssetFileDescriptor(mUri, "r")) {
+ return afd != null ? afd.getLength() : UNKNOWN_LENGTH;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to open asset file descriptor", e);
+ return UNKNOWN_LENGTH;
+ }
+ }
+
+ public MutableLiveData<Integer> getProgress() {
+ return mProgressLiveData;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ if (progress != null && progress.length > 0) {
+ mProgressLiveData.setValue(progress[0]);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(SessionInfo sessionInfo) {
+ if (sessionInfo == null || !sessionInfo.isActive()
+ || sessionInfo.getResolvedBaseApkPath() == null) {
+ Log.w(TAG, "Session info is invalid: " + sessionInfo);
+ mListener.onStagingFailure();
+ return;
+ }
+ mListener.onStagingSuccess(sessionInfo);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/TemporaryFileManager.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/TemporaryFileManager.java
new file mode 100644
index 000000000000..3a1c3973d474
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/TemporaryFileManager.java
@@ -0,0 +1,92 @@
+/*
+ * 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.packageinstaller.v2.model;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Manages files of the package installer and resets state during boot.
+ */
+public class TemporaryFileManager extends BroadcastReceiver {
+
+ private static final String LOG_TAG = TemporaryFileManager.class.getSimpleName();
+
+ /**
+ * Create a new file to hold a staged file.
+ *
+ * @param context The context of the caller
+ * @return A new file
+ */
+ @NonNull
+ public static File getStagedFile(@NonNull Context context) throws IOException {
+ return File.createTempFile("package", ".apk", context.getNoBackupFilesDir());
+ }
+
+ /**
+ * Get the file used to store the results of installs.
+ *
+ * @param context The context of the caller
+ * @return the file used to store the results of installs
+ */
+ @NonNull
+ public static File getInstallStateFile(@NonNull Context context) {
+ return new File(context.getNoBackupFilesDir(), "install_results.xml");
+ }
+
+ /**
+ * Get the file used to store the results of uninstalls.
+ *
+ * @param context The context of the caller
+ * @return the file used to store the results of uninstalls
+ */
+ @NonNull
+ public static File getUninstallStateFile(@NonNull Context context) {
+ return new File(context.getNoBackupFilesDir(), "uninstall_results.xml");
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ long systemBootTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+
+ File[] filesOnBoot = context.getNoBackupFilesDir().listFiles();
+
+ if (filesOnBoot == null) {
+ return;
+ }
+
+ for (int i = 0; i < filesOnBoot.length; i++) {
+ File fileOnBoot = filesOnBoot[i];
+
+ if (systemBootTime > fileOnBoot.lastModified()) {
+ boolean wasDeleted = fileOnBoot.delete();
+ if (!wasDeleted) {
+ Log.w(LOG_TAG, "Could not delete " + fileOnBoot.getName() + " onBoot");
+ }
+ } else {
+ Log.w(LOG_TAG, fileOnBoot.getName() + " was created before onBoot broadcast was "
+ + "received");
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
new file mode 100644
index 000000000000..520b6c573acf
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
@@ -0,0 +1,127 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class InstallAborted extends InstallStage {
+
+ public static final int ABORT_REASON_INTERNAL_ERROR = 0;
+ public static final int ABORT_REASON_POLICY = 1;
+ public static final int ABORT_REASON_DONE = 2;
+ public static final int DLG_PACKAGE_ERROR = 1;
+ private final int mStage = InstallStage.STAGE_ABORTED;
+ private final int mAbortReason;
+
+ /**
+ * It will hold the restriction name, when the restriction was enforced by the system, and not
+ * a device admin.
+ */
+ @NonNull
+ private final String mMessage;
+ /**
+ * <p>If abort reason is ABORT_REASON_POLICY, then this will hold the Intent
+ * to display a support dialog when a feature was disabled by an admin. It will be
+ * {@code null} if the feature is disabled by the system. In this case, the restriction name
+ * will be set in {@link #mMessage} </p>
+ *
+ * <p>If the abort reason is ABORT_REASON_INTERNAL_ERROR, it <b>may</b> hold an
+ * intent to be sent as a result to the calling activity.</p>
+ */
+ @Nullable
+ private final Intent mIntent;
+ private final int mErrorDialogType;
+ private final int mActivityResultCode;
+
+ private InstallAborted(int reason, @NonNull String message, @Nullable Intent intent,
+ int activityResultCode, int errorDialogType) {
+ mAbortReason = reason;
+ mMessage = message;
+ mIntent = intent;
+ mErrorDialogType = errorDialogType;
+ mActivityResultCode = activityResultCode;
+ }
+
+ public int getAbortReason() {
+ return mAbortReason;
+ }
+
+ @NonNull
+ public String getMessage() {
+ return mMessage;
+ }
+
+ @Nullable
+ public Intent getResultIntent() {
+ return mIntent;
+ }
+
+ public int getErrorDialogType() {
+ return mErrorDialogType;
+ }
+
+ public int getActivityResultCode() {
+ return mActivityResultCode;
+ }
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+
+ public static class Builder {
+
+ private final int mAbortReason;
+ private String mMessage = "";
+ private Intent mIntent = null;
+ private int mActivityResultCode = Activity.RESULT_CANCELED;
+ private int mErrorDialogType;
+
+ public Builder(int reason) {
+ mAbortReason = reason;
+ }
+
+ public Builder setMessage(@NonNull String message) {
+ mMessage = message;
+ return this;
+ }
+
+ public Builder setResultIntent(@NonNull Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ public Builder setErrorDialogType(int dialogType) {
+ mErrorDialogType = dialogType;
+ return this;
+ }
+
+ public Builder setActivityResultCode(int resultCode) {
+ mActivityResultCode = resultCode;
+ return this;
+ }
+
+ public InstallAborted build() {
+ return new InstallAborted(mAbortReason, mMessage, mIntent, mActivityResultCode,
+ mErrorDialogType);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
new file mode 100644
index 000000000000..67e169036551
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
@@ -0,0 +1,69 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
+
+public class InstallFailed extends InstallStage {
+
+ private final int mStage = InstallStage.STAGE_FAILED;
+ @NonNull
+ private final AppSnippet mAppSnippet;
+ private final int mStatusCode;
+ private final int mLegacyCode;
+ @Nullable
+ private final String mMessage;
+
+ public InstallFailed(@NonNull AppSnippet appSnippet, int statusCode, int legacyCode,
+ @Nullable String message) {
+ mAppSnippet = appSnippet;
+ mLegacyCode = statusCode;
+ mStatusCode = legacyCode;
+ mMessage = message;
+ }
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+
+ @NonNull
+ public Drawable getAppIcon() {
+ return mAppSnippet.getIcon();
+ }
+
+ @NonNull
+ public String getAppLabel() {
+ return (String) mAppSnippet.getLabel();
+ }
+
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ public int getLegacyCode() {
+ return mLegacyCode;
+ }
+
+ @Nullable
+ public String getMessage() {
+ return mMessage;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
new file mode 100644
index 000000000000..efd4947f712f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
@@ -0,0 +1,47 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
+
+public class InstallInstalling extends InstallStage {
+
+ private final int mStage = InstallStage.STAGE_INSTALLING;
+ @NonNull
+ private final AppSnippet mAppSnippet;
+
+ public InstallInstalling(@NonNull AppSnippet appSnippet) {
+ mAppSnippet = appSnippet;
+ }
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+
+ @NonNull
+ public Drawable getAppIcon() {
+ return mAppSnippet.getIcon();
+ }
+
+ @NonNull
+ public String getAppLabel() {
+ return (String) mAppSnippet.getLabel();
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
new file mode 100644
index 000000000000..548f2c544da7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
@@ -0,0 +1,27 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model.installstagedata;
+
+public class InstallReady extends InstallStage{
+
+ private final int mStage = InstallStage.STAGE_READY;
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
new file mode 100644
index 000000000000..f91e64bdc326
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
@@ -0,0 +1,34 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+public abstract class InstallStage {
+
+ public static final int STAGE_DEFAULT = -1;
+ public static final int STAGE_ABORTED = 0;
+ public static final int STAGE_STAGING = 1;
+ public static final int STAGE_READY = 2;
+ public static final int STAGE_USER_ACTION_REQUIRED = 3;
+ public static final int STAGE_INSTALLING = 4;
+ public static final int STAGE_SUCCESS = 5;
+ public static final int STAGE_FAILED = 6;
+
+ /**
+ * @return the integer value representing current install stage.
+ */
+ public abstract int getStageCode();
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
new file mode 100644
index 000000000000..a979cf87c350
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
@@ -0,0 +1,27 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+public class InstallStaging extends InstallStage {
+
+ private final int mStage = InstallStage.STAGE_STAGING;
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
new file mode 100644
index 000000000000..da482564c505
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
@@ -0,0 +1,95 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
+
+public class InstallSuccess extends InstallStage {
+
+ private final int mStage = InstallStage.STAGE_SUCCESS;
+
+ @NonNull
+ private final AppSnippet mAppSnippet;
+ private final boolean mShouldReturnResult;
+ /**
+ * <p>If the caller is requesting a result back, this will hold the Intent with
+ * EXTRA_INSTALL_RESULT set to INSTALL_SUCCEEDED which is sent back to the caller.</p>
+ * <p>If the caller doesn't want the result back, this will hold the Intent that launches
+ * the newly installed / updated app.</p>
+ */
+ @NonNull
+ private final Intent mResultIntent;
+
+ public InstallSuccess(@NonNull AppSnippet appSnippet, boolean shouldReturnResult,
+ @NonNull Intent launcherIntent) {
+ mAppSnippet = appSnippet;
+ mShouldReturnResult = shouldReturnResult;
+ mResultIntent = launcherIntent;
+ }
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+
+ @NonNull
+ public Drawable getAppIcon() {
+ return mAppSnippet.getIcon();
+ }
+
+ @NonNull
+ public String getAppLabel() {
+ return (String) mAppSnippet.getLabel();
+ }
+
+ public boolean shouldReturnResult() {
+ return mShouldReturnResult;
+ }
+
+ @NonNull
+ public Intent getResultIntent() {
+ return mResultIntent;
+ }
+
+ public static class Builder {
+
+ private final AppSnippet mAppSnippet;
+ private boolean mShouldReturnResult;
+ private Intent mLauncherIntent;
+
+ public Builder(@NonNull AppSnippet appSnippet) {
+ mAppSnippet = appSnippet;
+ }
+
+ public Builder setShouldReturnResult(boolean returnResult) {
+ mShouldReturnResult = returnResult;
+ return this;
+ }
+
+ public Builder setResultIntent(@NonNull Intent intent) {
+ mLauncherIntent = intent;
+ return this;
+ }
+
+ public InstallSuccess build() {
+ return new InstallSuccess(mAppSnippet, mShouldReturnResult, mLauncherIntent);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
new file mode 100644
index 000000000000..08a7487c69d3
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
@@ -0,0 +1,99 @@
+/*
+ * 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.packageinstaller.v2.model.installstagedata;
+
+import android.graphics.drawable.Drawable;
+import androidx.annotation.Nullable;
+import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
+
+public class InstallUserActionRequired extends InstallStage {
+
+ public static final int USER_ACTION_REASON_UNKNOWN_SOURCE = 0;
+ public static final int USER_ACTION_REASON_ANONYMOUS_SOURCE = 1;
+ public static final int USER_ACTION_REASON_INSTALL_CONFIRMATION = 2;
+ private final int mStage = InstallStage.STAGE_USER_ACTION_REQUIRED;
+ private final int mActionReason;
+ @Nullable
+ private final AppSnippet mAppSnippet;
+ private final boolean mIsAppUpdating;
+ @Nullable
+ private final String mDialogMessage;
+
+ public InstallUserActionRequired(int actionReason, @Nullable AppSnippet appSnippet,
+ boolean isUpdating, @Nullable String dialogMessage) {
+ mActionReason = actionReason;
+ mAppSnippet = appSnippet;
+ mIsAppUpdating = isUpdating;
+ mDialogMessage = dialogMessage;
+ }
+
+ @Override
+ public int getStageCode() {
+ return mStage;
+ }
+
+ @Nullable
+ public Drawable getAppIcon() {
+ return mAppSnippet != null ? mAppSnippet.getIcon() : null;
+ }
+
+ @Nullable
+ public String getAppLabel() {
+ return mAppSnippet != null ? (String) mAppSnippet.getLabel() : null;
+ }
+
+ public boolean isAppUpdating() {
+ return mIsAppUpdating;
+ }
+
+ @Nullable
+ public String getDialogMessage() {
+ return mDialogMessage;
+ }
+
+ public int getActionReason() {
+ return mActionReason;
+ }
+
+ public static class Builder {
+
+ private final int mActionReason;
+ private final AppSnippet mAppSnippet;
+ private boolean mIsAppUpdating;
+ private String mDialogMessage;
+
+ public Builder(int actionReason, @Nullable AppSnippet appSnippet) {
+ mActionReason = actionReason;
+ mAppSnippet = appSnippet;
+ }
+
+ public Builder setAppUpdating(boolean isUpdating) {
+ mIsAppUpdating = isUpdating;
+ return this;
+ }
+
+ public Builder setDialogMessage(@Nullable String message) {
+ mDialogMessage = message;
+ return this;
+ }
+
+ public InstallUserActionRequired build() {
+ return new InstallUserActionRequired(mActionReason, mAppSnippet, mIsAppUpdating,
+ mDialogMessage);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java
new file mode 100644
index 000000000000..fdb024ffc23e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.packageinstaller.v2.ui;
+
+import android.content.Intent;
+
+public interface InstallActionListener {
+
+ /**
+ * Method to handle a positive response from the user
+ */
+ void onPositiveResponse(int stageCode);
+
+ /**
+ * Method to dispatch intent for toggling "install from unknown sources" setting for a package
+ */
+ void sendUnknownAppsIntent(String packageName);
+
+ /**
+ * Method to handle a negative response from the user
+ */
+ void onNegativeResponse(int stageCode);
+ void openInstalledApp(Intent intent);
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
new file mode 100644
index 000000000000..949355535b64
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
@@ -0,0 +1,336 @@
+/*
+ * 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.packageinstaller.v2.ui;
+
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
+import static android.os.Process.INVALID_UID;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_INTERNAL_ERROR;
+import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_POLICY;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Window;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.InstallRepository;
+import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
+import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
+import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
+import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
+import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.ui.fragments.AnonymousSourceFragment;
+import com.android.packageinstaller.v2.ui.fragments.ExternalSourcesBlockedFragment;
+import com.android.packageinstaller.v2.ui.fragments.InstallConfirmationFragment;
+import com.android.packageinstaller.v2.ui.fragments.InstallFailedFragment;
+import com.android.packageinstaller.v2.ui.fragments.InstallInstallingFragment;
+import com.android.packageinstaller.v2.ui.fragments.InstallStagingFragment;
+import com.android.packageinstaller.v2.ui.fragments.InstallSuccessFragment;
+import com.android.packageinstaller.v2.ui.fragments.SimpleErrorFragment;
+import com.android.packageinstaller.v2.viewmodel.InstallViewModel;
+import com.android.packageinstaller.v2.viewmodel.InstallViewModelFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+public class InstallLaunch extends FragmentActivity implements InstallActionListener {
+
+ public static final String EXTRA_CALLING_PKG_UID =
+ InstallLaunch.class.getPackageName() + ".callingPkgUid";
+ public static final String EXTRA_CALLING_PKG_NAME =
+ InstallLaunch.class.getPackageName() + ".callingPkgName";
+ private static final String TAG = InstallLaunch.class.getSimpleName();
+ private static final String TAG_DIALOG = "dialog";
+ private final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
+ private final boolean mLocalLOGV = false;
+ /**
+ * A collection of unknown sources listeners that are actively listening for app ops mode
+ * changes
+ */
+ private final List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
+ private InstallViewModel mInstallViewModel;
+ private InstallRepository mInstallRepository;
+ private FragmentManager mFragmentManager;
+ private AppOpsManager mAppOpsManager;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ mFragmentManager = getSupportFragmentManager();
+ mAppOpsManager = getSystemService(AppOpsManager.class);
+
+ mInstallRepository = new InstallRepository(getApplicationContext());
+ mInstallViewModel = new ViewModelProvider(this,
+ new InstallViewModelFactory(this.getApplication(), mInstallRepository)).get(
+ InstallViewModel.class);
+
+ Intent intent = getIntent();
+ CallerInfo info = new CallerInfo(
+ intent.getStringExtra(EXTRA_CALLING_PKG_NAME),
+ intent.getIntExtra(EXTRA_CALLING_PKG_UID, INVALID_UID));
+ mInstallViewModel.preprocessIntent(intent, info);
+
+ mInstallViewModel.getCurrentInstallStage().observe(this, this::onInstallStageChange);
+ }
+
+ /**
+ * Main controller of the UI. This method shows relevant dialogs based on the install stage
+ */
+ private void onInstallStageChange(InstallStage installStage) {
+ if (installStage.getStageCode() == InstallStage.STAGE_STAGING) {
+ InstallStagingFragment stagingDialog = new InstallStagingFragment();
+ showDialogInner(stagingDialog);
+ mInstallViewModel.getStagingProgress().observe(this, stagingDialog::setProgress);
+ } else if (installStage.getStageCode() == InstallStage.STAGE_ABORTED) {
+ InstallAborted aborted = (InstallAborted) installStage;
+ switch (aborted.getAbortReason()) {
+ // TODO: check if any dialog is to be shown for ABORT_REASON_INTERNAL_ERROR
+ case InstallAborted.ABORT_REASON_DONE, InstallAborted.ABORT_REASON_INTERNAL_ERROR ->
+ setResult(aborted.getActivityResultCode(), aborted.getResultIntent(), true);
+ case InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted);
+ default -> setResult(RESULT_CANCELED, null, true);
+ }
+ } else if (installStage.getStageCode() == InstallStage.STAGE_USER_ACTION_REQUIRED) {
+ InstallUserActionRequired uar = (InstallUserActionRequired) installStage;
+ switch (uar.getActionReason()) {
+ case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION:
+ InstallConfirmationFragment actionDialog = new InstallConfirmationFragment(uar);
+ showDialogInner(actionDialog);
+ break;
+ case InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE:
+ ExternalSourcesBlockedFragment externalSourceDialog =
+ new ExternalSourcesBlockedFragment(uar);
+ showDialogInner(externalSourceDialog);
+ break;
+ case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE:
+ AnonymousSourceFragment anonymousSourceDialog = new AnonymousSourceFragment();
+ showDialogInner(anonymousSourceDialog);
+ }
+ } else if (installStage.getStageCode() == InstallStage.STAGE_INSTALLING) {
+ InstallInstalling installing = (InstallInstalling) installStage;
+ InstallInstallingFragment installingDialog = new InstallInstallingFragment(installing);
+ showDialogInner(installingDialog);
+ } else if (installStage.getStageCode() == InstallStage.STAGE_SUCCESS) {
+ InstallSuccess success = (InstallSuccess) installStage;
+ if (success.shouldReturnResult()) {
+ Intent successIntent = success.getResultIntent();
+ setResult(Activity.RESULT_OK, successIntent, true);
+ } else {
+ InstallSuccessFragment successFragment = new InstallSuccessFragment(success);
+ showDialogInner(successFragment);
+ }
+ } else if (installStage.getStageCode() == InstallStage.STAGE_FAILED) {
+ InstallFailed failed = (InstallFailed) installStage;
+ InstallFailedFragment failedDialog = new InstallFailedFragment(failed);
+ showDialogInner(failedDialog);
+ } else {
+ Log.d(TAG, "Unimplemented stage: " + installStage.getStageCode());
+ showDialogInner(null);
+ }
+ }
+
+ private void showPolicyRestrictionDialog(InstallAborted aborted) {
+ String restriction = aborted.getMessage();
+ Intent adminSupportIntent = aborted.getResultIntent();
+ boolean shouldFinish;
+
+ // If the given restriction is set by an admin, display information about the
+ // admin enforcing the restriction for the affected user. If not enforced by the admin,
+ // show the system dialog.
+ if (adminSupportIntent != null) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "Restriction set by admin, starting " + adminSupportIntent);
+ }
+ startActivity(adminSupportIntent);
+ // Finish the package installer app since the next dialog will not be shown by this app
+ shouldFinish = true;
+ } else {
+ if (mLocalLOGV) {
+ Log.i(TAG, "Restriction set by system: " + restriction);
+ }
+ DialogFragment blockedByPolicyDialog = createDevicePolicyRestrictionDialog(restriction);
+ // Don't finish the package installer app since the next dialog
+ // will be shown by this app
+ shouldFinish = false;
+ showDialogInner(blockedByPolicyDialog);
+ }
+ setResult(RESULT_CANCELED, null, shouldFinish);
+ }
+
+ /**
+ * Create a new dialog based on the install restriction enforced.
+ *
+ * @param restriction The restriction to create the dialog for
+ * @return The dialog
+ */
+ private DialogFragment createDevicePolicyRestrictionDialog(String restriction) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "createDialog(" + restriction + ")");
+ }
+ return switch (restriction) {
+ case UserManager.DISALLOW_INSTALL_APPS ->
+ new SimpleErrorFragment(R.string.install_apps_user_restriction_dlg_text);
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
+ new SimpleErrorFragment(R.string.unknown_apps_user_restriction_dlg_text);
+ default -> null;
+ };
+ }
+
+ /**
+ * Replace any visible dialog by the dialog returned by InstallRepository
+ *
+ * @param newDialog The new dialog to display
+ */
+ private void showDialogInner(@Nullable DialogFragment newDialog) {
+ DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(
+ TAG_DIALOG);
+ if (currentDialog != null) {
+ currentDialog.dismissAllowingStateLoss();
+ }
+ if (newDialog != null) {
+ newDialog.show(mFragmentManager, TAG_DIALOG);
+ }
+ }
+
+ public void setResult(int resultCode, Intent data, boolean shouldFinish) {
+ super.setResult(resultCode, data);
+ if (shouldFinish) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onPositiveResponse(int reasonCode) {
+ switch (reasonCode) {
+ case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE ->
+ mInstallViewModel.forcedSkipSourceCheck();
+ case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION ->
+ mInstallViewModel.initiateInstall();
+ }
+ }
+
+ @Override
+ public void onNegativeResponse(int stageCode) {
+ if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
+ mInstallViewModel.cleanupInstall();
+ }
+ setResult(Activity.RESULT_CANCELED, null, true);
+ }
+
+ @Override
+ public void sendUnknownAppsIntent(String sourcePackageName) {
+ Intent settingsIntent = new Intent();
+ settingsIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ final Uri packageUri = Uri.parse("package:" + sourcePackageName);
+ settingsIntent.setData(packageUri);
+ settingsIntent.setFlags(FLAG_ACTIVITY_NO_HISTORY);
+
+ try {
+ registerAppOpChangeListener(new UnknownSourcesListener(sourcePackageName),
+ sourcePackageName);
+ startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
+ } catch (ActivityNotFoundException exc) {
+ Log.e(TAG, "Settings activity not found for action: "
+ + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ }
+ }
+
+ @Override
+ public void openInstalledApp(Intent intent) {
+ setResult(RESULT_OK, intent, true);
+ if (intent != null && intent.hasCategory(CATEGORY_LAUNCHER)) {
+ startActivity(intent);
+ }
+ }
+
+ private void registerAppOpChangeListener(UnknownSourcesListener listener, String packageName) {
+ mAppOpsManager.startWatchingMode(
+ AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, packageName,
+ listener);
+ mActiveUnknownSourcesListeners.add(listener);
+ }
+
+ private void unregisterAppOpChangeListener(UnknownSourcesListener listener) {
+ mActiveUnknownSourcesListeners.remove(listener);
+ mAppOpsManager.stopWatchingMode(listener);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_TRUST_EXTERNAL_SOURCE) {
+ mInstallViewModel.reattemptInstall();
+ } else {
+ setResult(Activity.RESULT_CANCELED, null, true);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ while (!mActiveUnknownSourcesListeners.isEmpty()) {
+ unregisterAppOpChangeListener(mActiveUnknownSourcesListeners.get(0));
+ }
+ }
+
+ private class UnknownSourcesListener implements AppOpsManager.OnOpChangedListener {
+
+ private final String mOriginatingPackage;
+
+ public UnknownSourcesListener(String originatingPackage) {
+ mOriginatingPackage = originatingPackage;
+ }
+
+ @Override
+ public void onOpChanged(String op, String packageName) {
+ if (!mOriginatingPackage.equals(packageName)) {
+ return;
+ }
+ unregisterAppOpChangeListener(this);
+ mActiveUnknownSourcesListeners.remove(this);
+ if (isDestroyed()) {
+ return;
+ }
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ if (!isDestroyed()) {
+ // Bring Pia to the foreground. FLAG_ACTIVITY_REORDER_TO_FRONT will reuse the
+ // paused instance, so we don't unnecessarily create a new instance of Pia.
+ startActivity(getIntent()
+ .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
+ }
+ }, 500);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
new file mode 100644
index 000000000000..6d6fcc94faf7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
@@ -0,0 +1,63 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+/**
+ * Dialog to show when the source of apk can not be identified.
+ */
+public class AnonymousSourceFragment extends DialogFragment {
+
+ public static String TAG = AnonymousSourceFragment.class.getSimpleName();
+ private InstallActionListener mInstallActionListener;
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.anonymous_source_warning)
+ .setPositiveButton(R.string.anonymous_source_continue,
+ ((dialog, which) -> mInstallActionListener.onPositiveResponse(
+ InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE)))
+ .setNegativeButton(R.string.cancel,
+ ((dialog, which) -> mInstallActionListener.onNegativeResponse(
+ InstallStage.STAGE_USER_ACTION_REQUIRED))).create();
+ }
+
+ @Override
+ public void onCancel(@NonNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(InstallStage.STAGE_USER_ACTION_REQUIRED);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
new file mode 100644
index 000000000000..4cdce52e96ba
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
@@ -0,0 +1,72 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+/**
+ * Dialog to show when the installing app is an unknown source and needs AppOp grant to install
+ * other apps.
+ */
+public class ExternalSourcesBlockedFragment extends DialogFragment {
+
+ private final String TAG = ExternalSourcesBlockedFragment.class.getSimpleName();
+ private final InstallUserActionRequired mDialogData;
+ private InstallActionListener mInstallActionListener;
+
+ public ExternalSourcesBlockedFragment(InstallUserActionRequired dialogData) {
+ mDialogData = dialogData;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(requireContext())
+ .setTitle(mDialogData.getAppLabel())
+ .setIcon(mDialogData.getAppIcon())
+ .setMessage(R.string.untrusted_external_source_warning)
+ .setPositiveButton(R.string.external_sources_settings,
+ (dialog, which) -> mInstallActionListener.sendUnknownAppsIntent(
+ mDialogData.getDialogMessage()))
+ .setNegativeButton(R.string.cancel,
+ (dialog, which) -> mInstallActionListener.onNegativeResponse(
+ mDialogData.getStageCode()))
+ .create();
+ }
+
+ @Override
+ public void onCancel(@NonNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
new file mode 100644
index 000000000000..6398aef5d573
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -0,0 +1,95 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.Html;
+import android.view.View;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+/**
+ * Dialog to show when the requesting user confirmation for installing an app.
+ */
+public class InstallConfirmationFragment extends DialogFragment {
+
+ public static String TAG = InstallConfirmationFragment.class.getSimpleName();
+
+ @NonNull
+ private final InstallUserActionRequired mDialogData;
+ @NonNull
+ private InstallActionListener mInstallActionListener;
+
+ public InstallConfirmationFragment(@NonNull InstallUserActionRequired dialogData) {
+ mDialogData = dialogData;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+
+ AlertDialog dialog = new AlertDialog.Builder(requireContext())
+ .setIcon(mDialogData.getAppIcon())
+ .setTitle(mDialogData.getAppLabel())
+ .setView(dialogView)
+ .setPositiveButton(mDialogData.isAppUpdating() ? R.string.update : R.string.install,
+ (dialogInt, which) -> mInstallActionListener.onPositiveResponse(
+ InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION))
+ .setNegativeButton(R.string.cancel,
+ (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
+ mDialogData.getStageCode()))
+
+ .create();
+
+ // TODO: Dynamically change positive button text to update anyway
+ TextView viewToEnable;
+ if (mDialogData.isAppUpdating()) {
+ viewToEnable = dialogView.requireViewById(R.id.install_confirm_question_update);
+ String dialogMessage = mDialogData.getDialogMessage();
+ if (dialogMessage != null) {
+ viewToEnable.setText(Html.fromHtml(dialogMessage, Html.FROM_HTML_MODE_LEGACY));
+ }
+ } else {
+ viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
+ }
+ viewToEnable.setVisibility(View.VISIBLE);
+
+ return dialog;
+ }
+
+ @Override
+ public void onCancel(@NonNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
new file mode 100644
index 000000000000..d45cd76b2f2a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
@@ -0,0 +1,107 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInstaller;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+/**
+ * Dialog to show when the installation failed. Depending on the failure code, an appropriate
+ * message would be shown to the user. This dialog is shown only when the caller does not want the
+ * install result back.
+ */
+public class InstallFailedFragment extends DialogFragment {
+
+ private static final String TAG = InstallFailedFragment.class.getSimpleName();
+ private final InstallFailed mDialogData;
+ private InstallActionListener mInstallActionListener;
+
+ public InstallFailedFragment(InstallFailed dialogData) {
+ mDialogData = dialogData;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+ AlertDialog dialog = new AlertDialog.Builder(requireContext())
+ .setTitle(mDialogData.getAppLabel())
+ .setIcon(mDialogData.getAppIcon())
+ .setView(dialogView)
+ .setPositiveButton(R.string.done,
+ (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
+ mDialogData.getStageCode()))
+ .create();
+ setExplanationFromErrorCode(mDialogData.getStatusCode(), dialogView);
+
+ return dialog;
+ }
+
+ /**
+ * Unhide the appropriate label for the statusCode.
+ *
+ * @param statusCode The status code from the package installer.
+ */
+ private void setExplanationFromErrorCode(int statusCode, View dialogView) {
+ Log.d(TAG, "Installation status code: " + statusCode);
+
+ View viewToEnable;
+ switch (statusCode) {
+ case PackageInstaller.STATUS_FAILURE_BLOCKED:
+ viewToEnable = dialogView.requireViewById(R.id.install_failed_blocked);
+ break;
+ case PackageInstaller.STATUS_FAILURE_CONFLICT:
+ viewToEnable = dialogView.requireViewById(R.id.install_failed_conflict);
+ break;
+ case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
+ viewToEnable = dialogView.requireViewById(R.id.install_failed_incompatible);
+ break;
+ case PackageInstaller.STATUS_FAILURE_INVALID:
+ viewToEnable = dialogView.requireViewById(R.id.install_failed_invalid_apk);
+ break;
+ default:
+ viewToEnable = dialogView.requireViewById(R.id.install_failed);
+ break;
+ }
+
+ viewToEnable.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onCancel(@NonNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
new file mode 100644
index 000000000000..9f60f96bdfac
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
@@ -0,0 +1,64 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
+
+/**
+ * Dialog to show when an install is in progress.
+ */
+public class InstallInstallingFragment extends DialogFragment {
+
+ private final InstallInstalling mDialogData;
+ private AlertDialog mDialog;
+
+ public InstallInstallingFragment(InstallInstalling dialogData) {
+ mDialogData = dialogData;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+ mDialog = new AlertDialog.Builder(requireContext())
+ .setTitle(mDialogData.getAppLabel())
+ .setIcon(mDialogData.getAppIcon())
+ .setView(dialogView)
+ .setNegativeButton(R.string.cancel, null)
+ .create();
+
+ dialogView.requireViewById(R.id.installing).setVisibility(View.VISIBLE);
+ this.setCancelable(false);
+
+ return mDialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setEnabled(false);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java
new file mode 100644
index 000000000000..feb24282e0a5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallStagingFragment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ProgressBar;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+
+public class InstallStagingFragment extends DialogFragment {
+
+ private static final String TAG = InstallStagingFragment.class.getSimpleName();
+ private ProgressBar mProgressBar;
+ private AlertDialog mDialog;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+ dialogView.requireViewById(R.id.staging).setVisibility(View.VISIBLE);
+
+ mDialog = new AlertDialog.Builder(requireContext())
+ .setTitle(getString(R.string.app_name_unknown))
+ .setIcon(R.drawable.ic_file_download)
+ .setView(dialogView)
+ .setNegativeButton(R.string.cancel, null)
+ .setCancelable(false)
+ .create();
+
+ mDialog.setCanceledOnTouchOutside(false);
+ return mDialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setEnabled(false);
+ mProgressBar = mDialog.requireViewById(R.id.progress_indeterminate);
+ mProgressBar.setProgress(0);
+ mProgressBar.setMax(100);
+ mProgressBar.setIndeterminate(false);
+ }
+
+ public void setProgress(int progress) {
+ if (mProgressBar != null) {
+ mProgressBar.setProgress(progress);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
new file mode 100644
index 000000000000..ab6a93222d48
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
@@ -0,0 +1,100 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+import java.util.List;
+
+/**
+ * Dialog to show on a successful installation. This dialog is shown only when the caller does not
+ * want the install result back.
+ */
+public class InstallSuccessFragment extends DialogFragment {
+
+ private final InstallSuccess mDialogData;
+ private AlertDialog mDialog;
+ private InstallActionListener mInstallActionListener;
+ private PackageManager mPm;
+
+ public InstallSuccessFragment(InstallSuccess dialogData) {
+ mDialogData = dialogData;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ mPm = context.getPackageManager();
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
+ mDialog = new AlertDialog.Builder(requireContext()).setTitle(mDialogData.getAppLabel())
+ .setIcon(mDialogData.getAppIcon()).setView(dialogView).setNegativeButton(R.string.done,
+ (dialog, which) -> mInstallActionListener.onNegativeResponse(
+ InstallStage.STAGE_SUCCESS))
+ .setPositiveButton(R.string.launch, (dialog, which) -> {
+ }).create();
+
+ dialogView.requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
+
+ return mDialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ boolean enabled = false;
+ if (mDialogData.getResultIntent() != null) {
+ List<ResolveInfo> list = mPm.queryIntentActivities(mDialogData.getResultIntent(), 0);
+ if (list.size() > 0) {
+ enabled = true;
+ }
+ }
+ if (enabled) {
+ launchButton.setOnClickListener(view -> {
+ mInstallActionListener.openInstalledApp(mDialogData.getResultIntent());
+ });
+ } else {
+ launchButton.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onCancel(@NonNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
new file mode 100644
index 000000000000..47fd67f0cf6b
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
@@ -0,0 +1,62 @@
+/*
+ * 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.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+public class SimpleErrorFragment extends DialogFragment {
+
+ private static final String TAG = SimpleErrorFragment.class.getSimpleName();
+ private final int mMessageResId;
+ private InstallActionListener mInstallActionListener;
+
+ public SimpleErrorFragment(int messageResId) {
+ mMessageResId = messageResId;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ mInstallActionListener = (InstallActionListener) context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(mMessageResId)
+ .setPositiveButton(R.string.ok,
+ (dialog, which) ->
+ mInstallActionListener.onNegativeResponse(InstallStage.STAGE_ABORTED))
+ .create();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ mInstallActionListener.onNegativeResponse(InstallStage.STAGE_ABORTED);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
new file mode 100644
index 000000000000..759f4689996f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
@@ -0,0 +1,101 @@
+/*
+ * 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.packageinstaller.v2.viewmodel;
+
+import android.app.Application;
+import android.content.Intent;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.MediatorLiveData;
+import androidx.lifecycle.MutableLiveData;
+import com.android.packageinstaller.v2.model.InstallRepository;
+import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
+
+
+public class InstallViewModel extends AndroidViewModel {
+
+ private static final String TAG = InstallViewModel.class.getSimpleName();
+ private final InstallRepository mRepository;
+ private final MediatorLiveData<InstallStage> mCurrentInstallStage = new MediatorLiveData<>(
+ new InstallStaging());
+
+ public InstallViewModel(@NonNull Application application, InstallRepository repository) {
+ super(application);
+ mRepository = repository;
+ }
+
+ public MutableLiveData<InstallStage> getCurrentInstallStage() {
+ return mCurrentInstallStage;
+ }
+
+ public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
+ InstallStage stage = mRepository.performPreInstallChecks(intent, callerInfo);
+ if (stage.getStageCode() == InstallStage.STAGE_ABORTED) {
+ mCurrentInstallStage.setValue(stage);
+ } else {
+ // Since staging is an async operation, we will get the staging result later in time.
+ // Result of the file staging will be set in InstallRepository#mStagingResult.
+ // As such, mCurrentInstallStage will need to add another MutableLiveData
+ // as a data source
+ mRepository.stageForInstall();
+ mCurrentInstallStage.addSource(mRepository.getStagingResult(), installStage -> {
+ if (installStage.getStageCode() != InstallStage.STAGE_READY) {
+ mCurrentInstallStage.setValue(installStage);
+ } else {
+ checkIfAllowedAndInitiateInstall();
+ }
+ });
+ }
+ }
+
+ public MutableLiveData<Integer> getStagingProgress() {
+ return mRepository.getStagingProgress();
+ }
+
+ private void checkIfAllowedAndInitiateInstall() {
+ InstallStage stage = mRepository.requestUserConfirmation();
+ mCurrentInstallStage.setValue(stage);
+ }
+
+ public void forcedSkipSourceCheck() {
+ InstallStage stage = mRepository.forcedSkipSourceCheck();
+ mCurrentInstallStage.setValue(stage);
+ }
+
+ public void cleanupInstall() {
+ mRepository.cleanupInstall();
+ }
+
+ public void reattemptInstall() {
+ InstallStage stage = mRepository.reattemptInstall();
+ mCurrentInstallStage.setValue(stage);
+ }
+
+ public void initiateInstall() {
+ // Since installing is an async operation, we will get the install result later in time.
+ // Result of the installation will be set in InstallRepository#mInstallResult.
+ // As such, mCurrentInstallStage will need to add another MutableLiveData as a data source
+ mRepository.initiateInstall();
+ mCurrentInstallStage.addSource(mRepository.getInstallResult(), installStage -> {
+ if (installStage != null) {
+ mCurrentInstallStage.setValue(installStage);
+ }
+ });
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
new file mode 100644
index 000000000000..ef459e64d7d5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.packageinstaller.v2.viewmodel;
+
+import android.app.Application;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+import com.android.packageinstaller.v2.model.InstallRepository;
+
+public class InstallViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {
+
+ private final InstallRepository mRepository;
+ private final Application mApplication;
+
+ public InstallViewModelFactory(Application application, InstallRepository repository) {
+ // Calling super class' ctor ensures that create method is called correctly and the right
+ // ctor of InstallViewModel is used. If we fail to do that, the default ctor:
+ // InstallViewModel(application) is used, and repository isn't initialized in the viewmodel
+ super(application);
+ mApplication = application;
+ mRepository = repository;
+ }
+
+ @NonNull
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ return (T) new InstallViewModel(mApplication, mRepository);
+ }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 46a033d305aa..e3f8fbb88a65 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -47,12 +47,14 @@
android:textAppearance="?android:attr/textAppearanceListItem"
style="@style/MainSwitchText.Settingslib" />
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 6d1e76c5c512..255b2c92e709 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -49,12 +49,14 @@
android:lineBreakWordStyle="phrase"
style="@style/MainSwitchText.Settingslib" />
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index 5be0093b0b2c..bf34db93298b 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -38,11 +38,13 @@
android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
android:textAlignment="viewStart"/>
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/SwitchBar.Switch.Settingslib"/>
</LinearLayout>
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 b1e1585d4250..90c7d46c3004 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
@@ -49,6 +49,7 @@ import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvid
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
+import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -100,6 +101,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
CardPageProvider,
+ CopyablePageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index f52ceec41253..1d897f77011e 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -44,6 +44,7 @@ import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
+import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
@@ -71,6 +72,7 @@ object HomePageProvider : SettingsPageProvider {
AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CopyablePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CopyablePageProvider.kt
new file mode 100644
index 000000000000..f897d8c58030
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CopyablePageProvider.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.settingslib.spa.gallery.ui
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.common.EntrySearchData
+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.SettingsDimension
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.CopyableBody
+
+private const val TITLE = "Sample Copyable"
+
+object CopyablePageProvider : SettingsPageProvider {
+ override val name = "Copyable"
+
+ private val owner = createSettingsPage()
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ .setSearchDataFn { EntrySearchData(title = TITLE) }
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ CopyableBody(body = "Copyable body")
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index fdb0471755de..b40e911bd8de 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.1.2"
+agp = "8.1.4"
compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/screenshot/Android.bp b/packages/SettingsLib/Spa/screenshot/Android.bp
index bd508cbb9e2f..dbf4ce047825 100644
--- a/packages/SettingsLib/Spa/screenshot/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/Android.bp
@@ -18,6 +18,14 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "SpaScreenshotTestRNGFiles",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+}
+
android_test {
name: "SpaScreenshotTests",
use_resource_processor: true,
@@ -36,6 +44,7 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
+ "platform-parametric-runner-lib",
"platform-screenshot-diff-core",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
new file mode 100644
index 000000000000..6b8197c03e75
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
@@ -0,0 +1,74 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+ name: "SpaRoboRNGTestsAssetsLib",
+ asset_dirs: ["assets"],
+ sdk_version: "current",
+ platform_apis: true,
+ manifest: "AndroidManifest.xml",
+ optimize: {
+ enabled: false,
+ },
+ use_resource_processor: true,
+ resource_dirs: ["res"],
+}
+
+android_app {
+ name: "SpaRoboApp",
+ srcs: [],
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.appcompat_appcompat",
+ "flag-junit",
+ "guava",
+ "SpaLib",
+ "SpaLibTestUtils",
+ "SpaRoboRNGTestsAssetsLib",
+ "platform-screenshot-diff-core",
+ "PlatformComposeSceneTransitionLayoutTestsUtils",
+ ],
+ manifest: "robo-manifest.xml",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.settingslib.spa.screenshot",
+ ],
+ dont_merge_manifests: true,
+ platform_apis: true,
+ system_ext_specific: true,
+ certificate: "platform",
+ privileged: true,
+ resource_dirs: [],
+ kotlincflags: ["-Xjvm-default=all"],
+
+ plugins: ["dagger2-compiler"],
+ use_resource_processor: true,
+}
+
+android_robolectric_test {
+ name: "SpaRoboRNGTests",
+ srcs: [
+ ":SpaScreenshotTestRNGFiles",
+ ":flag-junit",
+ ":platform-test-screenshot-rules",
+ ],
+ // Do not add any new libraries here, they should be added to SpaRoboApp above.
+ static_libs: [
+ "androidx.compose.runtime_runtime",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
+ "inline-mockito-robolectric-prebuilt",
+ "platform-parametric-runner-lib",
+ "uiautomator-helpers",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "truth",
+ ],
+ upstream: true,
+ java_resource_dirs: ["config"],
+ instrumentation_for: "SpaRoboApp",
+}
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml b/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml
new file mode 100644
index 000000000000..1918e1c9b5c7
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.screenshot">
+
+ <uses-sdk android:minSdkVersion="21"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+</manifest>
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
new file mode 100644
index 000000000000..b2f3cf1fe737
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png
new file mode 100644
index 000000000000..dc7e756f08e3
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png
new file mode 100644
index 000000000000..95021fade418
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png
new file mode 100644
index 000000000000..281f572a7307
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png
new file mode 100644
index 000000000000..0dc707f9cf2f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
new file mode 100644
index 000000000000..63efaf557c3c
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png
new file mode 100644
index 000000000000..7c3e993b0fa4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
new file mode 100644
index 000000000000..3ac1d0f4572c
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
new file mode 100644
index 000000000000..105d1a108620
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
new file mode 100644
index 000000000000..a038779b0e79
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
new file mode 100644
index 000000000000..bb518c0f1cc5
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
new file mode 100644
index 000000000000..a042ffbdc4d0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
new file mode 100644
index 000000000000..ae0abdde0c4f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
new file mode 100644
index 000000000000..fa069275dbd4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png
new file mode 100644
index 000000000000..698cb4edd199
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png
new file mode 100644
index 000000000000..95021fade418
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
new file mode 100644
index 000000000000..a5f3fa5063d6
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png
new file mode 100644
index 000000000000..c1938b4adf01
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
new file mode 100644
index 000000000000..ae11f810c0bf
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png
new file mode 100644
index 000000000000..ffc6729c73bb
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
new file mode 100644
index 000000000000..9bc2b5d3b0c7
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
new file mode 100644
index 000000000000..fc845a63efb4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
new file mode 100644
index 000000000000..03db688f6799
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
new file mode 100644
index 000000000000..bb518c0f1cc5
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
new file mode 100644
index 000000000000..235df22934e7
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
new file mode 100644
index 000000000000..90442080fe22
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
new file mode 100644
index 000000000000..a3692cdc92f9
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
new file mode 100644
index 000000000000..233d088cf890
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
new file mode 100644
index 000000000000..10869f258b3b
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
new file mode 100644
index 000000000000..3eaecc14935f
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
new file mode 100644
index 000000000000..ca619117cbf4
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
new file mode 100644
index 000000000000..8333e68bfc13
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
new file mode 100644
index 000000000000..19d0afd6618a
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
new file mode 100644
index 000000000000..fcfd1d8763c1
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
new file mode 100644
index 000000000000..693c59243257
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
new file mode 100644
index 000000000000..1345c379cfb0
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
new file mode 100644
index 000000000000..5bb318fa0a91
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
new file mode 100644
index 000000000000..d0d014e5f8ed
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
new file mode 100644
index 000000000000..5bd1144b4e88
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties b/packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties
new file mode 100644
index 000000000000..83d7549551ce
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/config/robolectric.properties
@@ -0,0 +1,15 @@
+# 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.
+#
+sdk=NEWEST_SDK \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml b/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml
new file mode 100644
index 000000000000..6597ffb57688
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/res/drawable/accessibility_captioning_banner.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="412dp"
+ android:height="300dp"
+ android:viewportWidth="412"
+ android:viewportHeight="300">
+ <path
+ android:pathData="M383.9,300H28.1C12.6,300 0,287.4 0,271.9V28.1C0,12.6 12.6,0 28.1,0h355.8C399.4,0 412,12.6 412,28.1v243.8C412,287.4 399.4,300 383.9,300z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M79.2,179.6h53.6v8.5h-53.6z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.5,179.6h30.4v8.5h-30.4z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M79.2,195.5h79.2v8.5h-79.2z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M168.1,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M211.9,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M182.7,179.6h73.1v8.5h-73.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M265.5,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M302.1,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.7,67.9h-11.5c-1.6,0 -2.9,1.3 -2.9,2.9H67.8c-7.9,0 -14.4,6.5 -14.4,14.4v132.4c0,7.9 6.5,14.4 14.4,14.4h276.4c7.9,0 14.4,-6.5 14.4,-14.4V85.2c0,-7.9 -6.5,-14.4 -14.4,-14.4H203.1c0,-1.6 -1.3,-2.9 -2.9,-2.9h-28.8c-1.6,0 -2.9,1.3 -2.9,2.9h-23C145.5,69.2 144.3,67.9 142.7,67.9zM344.2,73.7c6.4,0 11.5,5.2 11.5,11.5v132.4c0,6.3 -5.2,11.5 -11.5,11.5H67.8c-6.4,0 -11.5,-5.2 -11.5,-11.5V85.2c0,-6.3 5.2,-11.5 11.5,-11.5H344.2z"
+ android:fillColor="#DADCE0"/>
+</vector>
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml b/packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml
new file mode 100644
index 000000000000..af1a11ed0a31
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/robotests/robo-manifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--- Include all the namespaces we will ever need anywhere, because this is the source the manifest merger uses for namespaces -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.settingslib.spa.screenshot"
+ coreApp="true">
+ <application>
+ <activity
+ android:name="androidx.activity.ComponentActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
index 3dcefe9c783d..ae85675ab1b8 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.spa.screenshot.util
+import android.os.Build
import androidx.activity.ComponentActivity
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -49,15 +50,19 @@ class SettingsScreenshotTestRule(
)
)
private val composeRule = createAndroidComposeRule<ComponentActivity>()
- private val delegateRule =
- RuleChain.outerRule(colorsRule)
- .around(deviceEmulationRule)
+ private val roboRule =
+ RuleChain.outerRule(deviceEmulationRule)
.around(screenshotRule)
.around(composeRule)
+ private val delegateRule =
+ RuleChain.outerRule(colorsRule)
+ .around(roboRule)
private val matcher = UnitTestBitmapMatcher
+ private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
override fun apply(base: Statement, description: Description): Statement {
- return delegateRule.apply(base, description)
+ val ruleToApply = if (isRobolectric) roboRule else delegateRule
+ return ruleToApply.apply(base, description)
}
/**
@@ -89,3 +94,18 @@ class SettingsScreenshotTestRule(
screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
}
}
+
+/** Create a [SettingsScreenshotTestRule] for settings screenshot tests. */
+fun settingsScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+): SettingsScreenshotTestRule {
+ val assetPath = if (Build.FINGERPRINT.contains("robolectric")) {
+ "frameworks/base/packages/SettingsLib/Spa/screenshot/robotests/assets"
+ } else {
+ "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
+ }
+ return SettingsScreenshotTestRule(
+ emulationSpec,
+ assetPath
+ )
+}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
index b74a2430c605..8f762f606f18 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
@@ -20,30 +20,30 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.WarningAmber
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButtons
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ActionButtonsScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java
new file mode 100644
index 000000000000..8e55695bc032
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.button;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
index 051ef7733a69..d766425fc12b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
@@ -17,7 +17,7 @@
package com.android.settingslib.spa.screenshot.widget.chart
import androidx.compose.material3.MaterialTheme
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.BarChart
import com.android.settingslib.spa.widget.chart.BarChartData
import com.android.settingslib.spa.widget.chart.BarChartModel
@@ -25,24 +25,24 @@ import com.github.mikephil.charting.formatter.IAxisValueFormatter
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class BarChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
index 3822571758fc..495bcbcc051e 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
@@ -16,7 +16,7 @@
package com.android.settingslib.spa.screenshot.widget.chart
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.LineChart
import com.android.settingslib.spa.widget.chart.LineChartData
import com.android.settingslib.spa.widget.chart.LineChartModel
@@ -25,24 +25,24 @@ import java.text.NumberFormat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class LineChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
index 6dd62ec03257..ee61aadd6262 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
@@ -16,31 +16,31 @@
package com.android.settingslib.spa.screenshot.widget.chart
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.PieChart
import com.android.settingslib.spa.widget.chart.PieChartData
import com.android.settingslib.spa.widget.chart.PieChartModel
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class PieChartScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java
new file mode 100644
index 000000000000..afe3f07a492e
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.chart;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
index 0ccfc0b12fca..94d032cbd507 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
@@ -17,31 +17,31 @@
package com.android.settingslib.spa.screenshot.widget.illustration
import com.android.settingslib.spa.screenshot.R
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.illustration.Illustration
import com.android.settingslib.spa.widget.illustration.IllustrationModel
import com.android.settingslib.spa.widget.illustration.ResourceType
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ImageIllustrationScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java
new file mode 100644
index 000000000000..0089c2e26e34
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.illustration;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
index e547d260cbdb..2a01d8496158 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
@@ -17,30 +17,30 @@
package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class MainSwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
index dd6b553f714d..4d8650efe201 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
@@ -21,22 +21,23 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Autorenew
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsIcon
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class PreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
private const val TITLE = "Title"
@@ -47,9 +48,8 @@ class PreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
index 357d81593cb6..3983cc08ebc3 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
@@ -21,7 +21,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.SystemUpdate
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.ProgressBarPreference
import com.android.settingslib.spa.widget.preference.ProgressBarPreferenceModel
import com.android.settingslib.spa.widget.preference.ProgressBarWithDataPreference
@@ -29,24 +29,24 @@ import com.android.settingslib.spa.widget.ui.CircularProgressBar
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class ProgressBarPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
index fdee7ee70483..3a96a7090d9b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
@@ -19,30 +19,30 @@ package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessAlarm
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SliderPreference
import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SliderPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
index 6966a74b0af5..4a8064ac21d4 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
@@ -20,31 +20,31 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsIcon
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
index 72c5cb81962e..91b7b2429fca 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
@@ -18,30 +18,30 @@ package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class TwoTargetSwitchPreferenceScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java
new file mode 100644
index 000000000000..fd6a5dda5ca8
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.preference;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
index fb01f7715def..6ba010f5f96f 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
@@ -16,29 +16,29 @@
package com.android.settingslib.spa.screenshot.widget.ui
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.ui.Footer
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class FooterScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
index 2867741e2e0a..320b20703c38 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
@@ -16,30 +16,30 @@
package com.android.settingslib.spa.screenshot.widget.ui
-import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
+import com.android.settingslib.spa.screenshot.util.settingsScreenshotTestRule
import com.android.settingslib.spa.widget.ui.Spinner
import com.android.settingslib.spa.widget.ui.SpinnerOption
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SpinnerScreenshotTest(emulationSpec: DeviceEmulationSpec) {
companion object {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
}
@get:Rule
val screenshotRule =
- SettingsScreenshotTestRule(
+ settingsScreenshotTestRule(
emulationSpec,
- "frameworks/base/packages/SettingsLib/Spa/screenshot/assets"
)
@Test
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java
new file mode 100644
index 000000000000..45210abad88e
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.settingslib.spa.screenshot.widget.ui;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 47660bc44cc8..5a1120e6c7a1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.dp
object SettingsDimension {
+ val paddingTiny = 2.dp
val paddingSmall = 4.dp
val itemIconSize = 24.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/CopyableBody.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/CopyableBody.kt
new file mode 100644
index 000000000000..930d0a1872ab
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/CopyableBody.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.ui
+
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.DpOffset
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun CopyableBody(body: String) {
+ var expanded by remember { mutableStateOf(false) }
+ var dpOffset by remember { mutableStateOf(DpOffset.Unspecified) }
+
+ Box(modifier = Modifier
+ .fillMaxWidth()
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onLongPress = {
+ dpOffset = DpOffset(it.x.toDp(), it.y.toDp())
+ expanded = true
+ },
+ )
+ }
+ ) {
+ SettingsBody(body)
+
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ offset = dpOffset,
+ ) {
+ DropdownMenuTitle(body)
+ DropdownMenuCopy(body) { expanded = false }
+ }
+ }
+}
+
+@Composable
+private fun DropdownMenuTitle(text: String) {
+ Text(
+ text = text,
+ modifier = Modifier
+ .padding(MenuDefaults.DropdownMenuItemContentPadding)
+ .padding(
+ top = SettingsDimension.itemPaddingAround,
+ bottom = SettingsDimension.buttonPaddingVertical,
+ ),
+ color = SettingsTheme.colorScheme.categoryTitle,
+ style = MaterialTheme.typography.labelMedium,
+ )
+}
+
+@Composable
+private fun DropdownMenuCopy(body: String, onCopy: () -> Unit) {
+ val clipboardManager = LocalClipboardManager.current
+ DropdownMenuItem(
+ text = {
+ Text(
+ text = stringResource(android.R.string.copy),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ },
+ onClick = {
+ onCopy()
+ clipboardManager.setText(AnnotatedString(body))
+ }
+ )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index f4b284362c52..b4a6a0d00720 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -39,6 +39,7 @@ import com.android.settingslib.spa.framework.theme.toMediumWeight
fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
Text(
text = title,
+ modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium.withWeight(useMediumWeight),
)
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
new file mode 100644
index 000000000000..71072a5d58c5
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.ui
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.longClick
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTouchInput
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CopyableBodyTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun text_isDisplayed() {
+ composeTestRule.setContent {
+ CopyableBody(TEXT)
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun onLongPress_contextMenuDisplayed() {
+ composeTestRule.setContent {
+ CopyableBody(TEXT)
+ }
+
+ composeTestRule.onNodeWithText(TEXT).performTouchInput {
+ longClick()
+ }
+
+ composeTestRule.onNodeWithText(context.getString(android.R.string.copy)).assertIsDisplayed()
+ }
+
+ @Test
+ fun onCopy_saveToClipboard() {
+ val clipboardManager = context.getSystemService(ClipboardManager::class.java)!!
+ clipboardManager.setPrimaryClip(ClipData.newPlainText("", ""))
+ composeTestRule.setContent {
+ CopyableBody(TEXT)
+ }
+
+ composeTestRule.onNodeWithText(TEXT).performTouchInput {
+ longClick()
+ }
+ composeTestRule.onNodeWithText(context.getString(android.R.string.copy)).performClick()
+
+ assertThat(clipboardManager.primaryClip!!.getItemAt(0).text.toString()).isEqualTo(TEXT)
+ }
+
+ private companion object {
+ const val TEXT = "Text"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
new file mode 100644
index 000000000000..d0d2dc0083a6
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.spaprivileged.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * A [BroadcastReceiver] flow for the given [intentFilter].
+ */
+fun Context.broadcastReceiverFlow(intentFilter: IntentFilter): Flow<Intent> = callbackFlow {
+ val broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ trySend(intent)
+ }
+ }
+ registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
+
+ awaitClose { unregisterReceiver(broadcastReceiver) }
+}.conflate().flowOn(Dispatchers.Default)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index 352503742b9c..5baf7be98666 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -16,20 +16,22 @@
package com.android.settingslib.spaprivileged.model.enterprise
-import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER
import android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER
import android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER
import android.content.Context
import android.content.pm.UserInfo
import com.android.settingslib.R
+import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
-class EnterpriseRepository(private val context: Context) {
- private val resources by lazy {
- checkNotNull(context.getSystemService(DevicePolicyManager::class.java)).resources
- }
+interface IEnterpriseRepository {
+ fun getEnterpriseString(updatableStringId: String, resId: Int): String
+}
+
+class EnterpriseRepository(private val context: Context) : IEnterpriseRepository {
+ private val resources by lazy { context.devicePolicyManager.resources }
- fun getEnterpriseString(updatableStringId: String, resId: Int): String =
+ override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
fun getProfileTitle(userInfo: UserInfo): String = if (userInfo.isManagedProfile) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
new file mode 100644
index 000000000000..3acc9ad83d2c
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.enterprise
+
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.widget.restricted.R
+
+sealed interface RestrictedMode
+
+data object NoRestricted : RestrictedMode
+
+data object BaseUserRestricted : RestrictedMode
+
+interface BlockedByAdmin : RestrictedMode {
+ fun getSummary(checked: Boolean?): String
+ fun sendShowAdminSupportDetailsIntent()
+}
+
+internal data class BlockedByAdminImpl(
+ private val context: Context,
+ private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin,
+ private val enterpriseRepository: IEnterpriseRepository = EnterpriseRepository(context),
+) : BlockedByAdmin {
+ override fun getSummary(checked: Boolean?) = when (checked) {
+ true -> enterpriseRepository.getEnterpriseString(
+ updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
+ resId = R.string.enabled_by_admin,
+ )
+
+ false -> enterpriseRepository.getEnterpriseString(
+ updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
+ resId = R.string.disabled_by_admin,
+ )
+
+ else -> ""
+ }
+
+ override fun sendShowAdminSupportDetailsIntent() {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin)
+ }
+}
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 09cb98e22af3..550966beb0b8 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
@@ -16,7 +16,6 @@
package com.android.settingslib.spaprivileged.model.enterprise
-import android.app.admin.DevicePolicyResources.Strings.Settings
import android.content.Context
import android.os.UserHandle
import android.os.UserManager
@@ -25,55 +24,16 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.RestrictedLockUtils
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
-import com.android.settingslib.widget.restricted.R
data class Restrictions(
val userId: Int = UserHandle.myUserId(),
val keys: List<String>,
)
-sealed interface RestrictedMode
-
-data object NoRestricted : RestrictedMode
-
-data object BaseUserRestricted : RestrictedMode
-
-interface BlockedByAdmin : RestrictedMode {
- fun getSummary(checked: Boolean?): String
- fun sendShowAdminSupportDetailsIntent()
-}
-
-private data class BlockedByAdminImpl(
- private val context: Context,
- private val enforcedAdmin: EnforcedAdmin,
-) : BlockedByAdmin {
- private val enterpriseRepository by lazy { EnterpriseRepository(context) }
-
- override fun getSummary(checked: Boolean?) = when (checked) {
- true -> enterpriseRepository.getEnterpriseString(
- updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
- resId = R.string.enabled_by_admin,
- )
-
- false -> enterpriseRepository.getEnterpriseString(
- updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
- resId = R.string.disabled_by_admin,
- )
-
- else -> ""
- }
-
- override fun sendShowAdminSupportDetailsIntent() {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin)
- }
-}
-
interface RestrictionsProvider {
@Composable
fun restrictedModeState(): State<RestrictedMode?>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index fc10a27cfa5b..45295b013cf9 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -36,10 +36,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import com.android.settingslib.development.DevelopmentSettingsEnabler
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.ui.CopyableBody
import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsTitle
import com.android.settingslib.spaprivileged.R
@@ -71,26 +71,38 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
@Composable
private fun InstallType(app: ApplicationInfo) {
if (!app.isInstantApp) return
- Spacer(modifier = Modifier.height(4.dp))
- SettingsBody(stringResource(com.android.settingslib.widget.preference.app.R.string.install_type_instant))
+ Spacer(modifier = Modifier.height(SettingsDimension.paddingSmall))
+ SettingsBody(
+ stringResource(
+ com.android.settingslib.widget.preference.app.R.string.install_type_instant
+ )
+ )
}
@Composable
private fun AppVersion() {
- if (packageInfo.versionName == null) return
- Spacer(modifier = Modifier.height(4.dp))
- SettingsBody(packageInfo.versionNameBidiWrapped)
+ val versionName = packageInfo.versionNameBidiWrapped ?: return
+ Spacer(modifier = Modifier.height(SettingsDimension.paddingSmall))
+ SettingsBody(versionName)
}
@Composable
fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
- if (packageInfo.versionName == null) return
- HorizontalDivider()
- Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
- SettingsBody(stringResource(R.string.version_text, packageInfo.versionNameBidiWrapped))
+ val context = LocalContext.current
+ val footer = remember(showPackageName) {
+ val list = mutableListOf<String>()
+ packageInfo.versionNameBidiWrapped?.let {
+ list += context.getString(R.string.version_text, it)
+ }
if (showPackageName) {
- SettingsBody(packageInfo.packageName)
+ list += packageInfo.packageName
}
+ list.joinToString(separator = System.lineSeparator())
+ }
+ if (footer.isBlank()) return
+ HorizontalDivider()
+ Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ CopyableBody(footer)
}
}
@@ -104,7 +116,7 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
private companion object {
/** Wrapped the version name, so its directionality still keep same when RTL. */
- val PackageInfo.versionNameBidiWrapped: String
+ val PackageInfo.versionNameBidiWrapped: String?
get() = BidiFormatter.getInstance().unicodeWrap(versionName)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
index 7bd7fbe0c526..06b3eabfad26 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
@@ -16,9 +16,7 @@
package com.android.settingslib.spaprivileged.template.app
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_DEFAULT
-import android.app.AppOpsManager.MODE_ERRORED
+import android.app.AppOpsManager
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
@@ -64,7 +62,7 @@ abstract class AppOpPermissionListModel(
*/
open val permissionHasAppOpFlag: Boolean = true
- open val modeForNotAllowed: Int = MODE_ERRORED
+ open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED
/**
* Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed.
@@ -130,27 +128,14 @@ abstract class AppOpPermissionListModel(
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) =
recordListFlow.filterItem(::isChangeable)
- /**
- * Defining the default behavior as permissible as long as the package requested this permission
- * (This means pre-M gets approval during install time; M apps gets approval during runtime).
- */
@Composable
- override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? {
- if (record.hasRequestBroaderPermission) {
- // Broader permission trumps the specific permission.
- return { true }
- }
-
- val mode = record.appOpsController.mode.observeAsState()
- return {
- when (mode.value) {
- null -> null
- MODE_ALLOWED -> true
- MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) }
- else -> false
- }
- }
- }
+ override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? =
+ isAllowed(
+ record = record,
+ appOpsController = record.appOpsController,
+ permission = permission,
+ packageManagers = packageManagers,
+ )
override fun isChangeable(record: AppOpPermissionRecord) =
record.hasRequestPermission &&
@@ -161,3 +146,33 @@ abstract class AppOpPermissionListModel(
record.appOpsController.setAllowed(newAllowed)
}
}
+
+/**
+ * Defining the default behavior as permissible as long as the package requested this permission
+ * (This means pre-M gets approval during install time; M apps gets approval during runtime).
+ */
+@Composable
+internal fun isAllowed(
+ record: AppOpPermissionRecord,
+ appOpsController: IAppOpsController,
+ permission: String,
+ packageManagers: IPackageManagers = PackageManagers,
+): () -> Boolean? {
+ if (record.hasRequestBroaderPermission) {
+ // Broader permission trumps the specific permission.
+ return { true }
+ }
+
+ val mode = appOpsController.mode.observeAsState()
+ return {
+ when (mode.value) {
+ null -> null
+ AppOpsManager.MODE_ALLOWED -> true
+ AppOpsManager.MODE_DEFAULT -> {
+ with(packageManagers) { record.app.hasGrantPermission(permission) }
+ }
+
+ else -> false
+ }
+ }
+}
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 3380b7db4cd0..565543614866 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
@@ -16,19 +16,16 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.os.bundleOf
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.SettingsEntry
@@ -49,6 +46,8 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvid
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
internal class TogglePermissionAppInfoPageProvider(
private val appListTemplate: TogglePermissionAppListTemplate,
@@ -132,7 +131,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
@VisibleForTesting
@Composable
-internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage(
+internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPage(
packageName: String,
userId: Int,
packageManagers: IPackageManagers = PackageManagers,
@@ -145,40 +144,34 @@ internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfo
footerContent = { AnnotatedText(footerResId) },
packageManagers = packageManagers,
) {
- val model = createSwitchModel(checkNotNull(applicationInfo))
+ val app = applicationInfo ?: return@AppInfoPage
+ val record = rememberRecord(app).value ?: return@AppInfoPage
+ val isAllowed = isAllowed(record)
+ val isChangeable by rememberIsChangeable(record)
+ val switchModel = object : SwitchPreferenceModel {
+ override val title = stringResource(switchTitleResId)
+ override val checked = isAllowed
+ override val changeable = { isChangeable }
+ override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) }
+ }
val restrictions = Restrictions(userId, switchRestrictionKeys)
- RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
+ RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
}
}
@Composable
-private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel(
- app: ApplicationInfo,
-): TogglePermissionSwitchModel<T> {
- val context = LocalContext.current
- val record = remember(app) { transformItem(app) }
- val isAllowed = isAllowed(record)
- return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) }
- .also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } }
-}
-
-private class TogglePermissionSwitchModel<T : AppRecord>(
- context: Context,
- private val listModel: TogglePermissionAppListModel<T>,
- private val record: T,
- isAllowed: () -> Boolean?,
-) : SwitchPreferenceModel {
- private var appChangeable by mutableStateOf(true)
+private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: ApplicationInfo) =
+ remember(app) {
+ flow {
+ emit(transformItem(app))
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(initialValue = null)
- override val title: String = context.getString(listModel.switchTitleResId)
- override val checked = isAllowed
- override val changeable = { appChangeable }
- fun initState() {
- appChangeable = listModel.isChangeable(record)
- }
-
- override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
- listModel.setAllowed(record, newChecked)
- }
-}
+@Composable
+private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) =
+ remember(record) {
+ flow {
+ emit(isChangeable(record))
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(initialValue = false)
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 785f77981872..36c91f463efe 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
@@ -45,7 +45,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
-import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel
import kotlinx.coroutines.flow.Flow
private const val ENTRY_NAME = "AppList"
@@ -157,7 +157,7 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
}
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
- return RestrictedSwitchPreference.getSummary(
+ return RestrictedSwitchPreferenceModel.getSummary(
context = context,
restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 5447f21fd417..223e99e61204 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,16 +57,24 @@ fun UserProfilePager(content: @Composable (userGroup: UserGroup) -> Unit) {
private fun UserManager.getUserGroups(): List<UserGroup> {
val userGroupList = mutableListOf<UserGroup>()
- val profileToShowInSettingsList = getProfiles(UserHandle.myUserId())
- .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle).showInSettings }
+ val showInSettingsMap = getProfiles(UserHandle.myUserId()).groupBy { showInSettings(it) }
- profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
- .takeIf { it.isNotEmpty() }
- ?.map { it.first }
- ?.let { userInfos -> userGroupList += UserGroup(userInfos) }
+ showInSettingsMap[UserProperties.SHOW_IN_SETTINGS_WITH_PARENT]?.let {
+ userGroupList += UserGroup(it)
+ }
- profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_LAUNCHER_SEPARATE }
- .forEach { userGroupList += UserGroup(userInfos = listOf(it.first)) }
+ showInSettingsMap[UserProperties.SHOW_IN_SETTINGS_SEPARATE]?.forEach {
+ userGroupList += UserGroup(listOf(it))
+ }
return userGroupList
}
+
+private fun UserManager.showInSettings(userInfo: UserInfo): Int {
+ val userProperties = getUserProperties(userInfo.userHandle)
+ return if (userInfo.isQuietModeEnabled && userProperties.hideInSettingsInQuietMode) {
+ UserProperties.SHOW_IN_SETTINGS_NO
+ } else {
+ userProperties.showInSettings
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreference.kt
new file mode 100644
index 000000000000..125f636507a8
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreference.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.spaprivileged.template.preference
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.widget.preference.MainSwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper
+
+@Composable
+fun RestrictedMainSwitchPreference(model: SwitchPreferenceModel, restrictions: Restrictions) {
+ RestrictedMainSwitchPreference(model, restrictions, ::RestrictionsProviderImpl)
+}
+
+@VisibleForTesting
+@Composable
+internal fun RestrictedMainSwitchPreference(
+ model: SwitchPreferenceModel,
+ restrictions: Restrictions,
+ restrictionsProviderFactory: RestrictionsProviderFactory,
+) {
+ if (restrictions.keys.isEmpty()) {
+ MainSwitchPreference(model)
+ return
+ }
+ restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) {
+ MainSwitchPreference(it)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index e41976f79f8f..d5c5574a0450 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -16,29 +16,14 @@
package com.android.settingslib.spaprivileged.template.preference
-import android.content.Context
import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.toggleableState
-import androidx.compose.ui.state.ToggleableState
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
-import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
-import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
-import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
-import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
-import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper
@Composable
fun RestrictedSwitchPreference(
@@ -59,91 +44,7 @@ internal fun RestrictedSwitchPreference(
SwitchPreference(model)
return
}
- val context = LocalContext.current
- val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
- val restrictedSwitchModel = remember(restrictedMode) {
- RestrictedSwitchPreferenceModel(context, model, restrictedMode)
- }
- restrictedSwitchModel.RestrictionWrapper {
- SwitchPreference(restrictedSwitchModel)
- }
-}
-
-internal object RestrictedSwitchPreference {
- fun getSummary(
- context: Context,
- restrictedModeSupplier: () -> RestrictedMode?,
- summaryIfNoRestricted: () -> String,
- checked: () -> Boolean?,
- ): () -> String = {
- when (val restrictedMode = restrictedModeSupplier()) {
- is NoRestricted -> summaryIfNoRestricted()
- is BaseUserRestricted -> context.getString(com.android.settingslib.R.string.disabled)
- is BlockedByAdmin -> restrictedMode.getSummary(checked())
- null -> context.getPlaceholder()
- }
- }
-}
-
-private class RestrictedSwitchPreferenceModel(
- context: Context,
- model: SwitchPreferenceModel,
- private val restrictedMode: RestrictedMode?,
-) : SwitchPreferenceModel {
- override val title = model.title
-
- override val summary = RestrictedSwitchPreference.getSummary(
- context = context,
- restrictedModeSupplier = { restrictedMode },
- summaryIfNoRestricted = model.summary,
- checked = model.checked,
- )
-
- override val checked = when (restrictedMode) {
- null -> ({ null })
- is NoRestricted -> model.checked
- is BaseUserRestricted -> ({ false })
- is BlockedByAdmin -> model.checked
- }
-
- override val changeable = when (restrictedMode) {
- null -> ({ false })
- is NoRestricted -> model.changeable
- is BaseUserRestricted -> ({ false })
- is BlockedByAdmin -> ({ false })
- }
-
- override val onCheckedChange = when (restrictedMode) {
- null -> null
- is NoRestricted -> model.onCheckedChange
- // Need to passthrough onCheckedChange for toggleable semantics, although since changeable
- // is false so this will not be called.
- is BaseUserRestricted -> model.onCheckedChange
- // Pass null since semantics ToggleableState is provided in RestrictionWrapper.
- is BlockedByAdmin -> null
- }
-
- @Composable
- fun RestrictionWrapper(content: @Composable () -> Unit) {
- if (restrictedMode !is BlockedByAdmin) {
- content()
- return
- }
- Box(
- Modifier
- .clickable(
- role = Role.Switch,
- onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
- )
- .semantics {
- this.toggleableState = ToggleableState(checked())
- },
- ) { content() }
- }
-
- private fun ToggleableState(value: Boolean?) = when (value) {
- true -> ToggleableState.On
- false -> ToggleableState.Off
- null -> ToggleableState.Indeterminate
+ restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) {
+ SwitchPreference(it)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
new file mode 100644
index 000000000000..fa44ecb92ed5
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.spaprivileged.template.preference
+
+import android.content.Context
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.state.ToggleableState
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
+
+internal class RestrictedSwitchPreferenceModel(
+ context: Context,
+ model: SwitchPreferenceModel,
+ private val restrictedMode: RestrictedMode?,
+) : SwitchPreferenceModel {
+ override val title = model.title
+
+ override val summary = getSummary(
+ context = context,
+ restrictedModeSupplier = { restrictedMode },
+ summaryIfNoRestricted = model.summary,
+ checked = model.checked,
+ )
+
+ override val checked = when (restrictedMode) {
+ null -> ({ null })
+ is NoRestricted -> model.checked
+ is BaseUserRestricted -> ({ false })
+ is BlockedByAdmin -> model.checked
+ }
+
+ override val changeable = if (restrictedMode is NoRestricted) model.changeable else ({ false })
+
+ override val onCheckedChange = when (restrictedMode) {
+ null -> null
+ is NoRestricted -> model.onCheckedChange
+ // Need to passthrough onCheckedChange for toggleable semantics, although since changeable
+ // is false so this will not be called.
+ is BaseUserRestricted -> model.onCheckedChange
+ // Pass null since semantics ToggleableState is provided in RestrictionWrapper.
+ is BlockedByAdmin -> null
+ }
+
+ @Composable
+ fun RestrictionWrapper(content: @Composable () -> Unit) {
+ if (restrictedMode !is BlockedByAdmin) {
+ content()
+ return
+ }
+ Box(
+ Modifier
+ .clickable(
+ role = Role.Switch,
+ onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
+ )
+ .semantics {
+ this.toggleableState = ToggleableState(checked())
+ },
+ ) { content() }
+ }
+
+ private fun ToggleableState(value: Boolean?) = when (value) {
+ true -> ToggleableState.On
+ false -> ToggleableState.Off
+ null -> ToggleableState.Indeterminate
+ }
+
+ companion object {
+ @Composable
+ fun RestrictionsProviderFactory.RestrictedSwitchWrapper(
+ model: SwitchPreferenceModel,
+ restrictions: Restrictions,
+ content: @Composable (SwitchPreferenceModel) -> Unit,
+ ) {
+ val context = LocalContext.current
+ val restrictedMode = rememberRestrictedMode(restrictions).value
+ val restrictedSwitchPreferenceModel = remember(restrictedMode) {
+ RestrictedSwitchPreferenceModel(context, model, restrictedMode)
+ }
+ restrictedSwitchPreferenceModel.RestrictionWrapper {
+ content(restrictedSwitchPreferenceModel)
+ }
+ }
+
+ fun getSummary(
+ context: Context,
+ restrictedModeSupplier: () -> RestrictedMode?,
+ summaryIfNoRestricted: () -> String,
+ checked: () -> Boolean?,
+ ): () -> String = {
+ when (val restrictedMode = restrictedModeSupplier()) {
+ is NoRestricted -> summaryIfNoRestricted()
+ is BaseUserRestricted ->
+ context.getString(com.android.settingslib.R.string.disabled)
+
+ is BlockedByAdmin -> restrictedMode.getSummary(checked())
+ null -> context.getPlaceholder()
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
new file mode 100644
index 000000000000..dfaf3c66ff8d
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.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.settingslib.spaprivileged.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class BroadcastReceiverFlowTest {
+
+ private var registeredBroadcastReceiver: BroadcastReceiver? = null
+
+ private val context = mock<Context> {
+ on {
+ registerReceiver(any(), eq(INTENT_FILTER), eq(Context.RECEIVER_NOT_EXPORTED))
+ } doAnswer {
+ registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
+ null
+ }
+ }
+
+ @Test
+ fun broadcastReceiverFlow_registered() = runBlocking {
+ val flow = context.broadcastReceiverFlow(INTENT_FILTER)
+
+ flow.firstWithTimeoutOrNull()
+
+ assertThat(registeredBroadcastReceiver).isNotNull()
+ }
+
+ @Test
+ fun broadcastReceiverFlow_isCalledOnReceive() = runBlocking {
+ var onReceiveIsCalled = false
+ launch {
+ context.broadcastReceiverFlow(INTENT_FILTER).first {
+ onReceiveIsCalled = true
+ true
+ }
+ }
+
+ delay(100)
+ registeredBroadcastReceiver!!.onReceive(context, Intent())
+ delay(100)
+
+ assertThat(onReceiveIsCalled).isTrue()
+ }
+
+ private companion object {
+ val INTENT_FILTER = IntentFilter()
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
new file mode 100644
index 000000000000..8fd16b37bfeb
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.spaprivileged.model.enterprise
+
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.RestrictedLockUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RestrictedModeTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val fakeEnterpriseRepository = object : IEnterpriseRepository {
+ override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
+ when (updatableStringId) {
+ Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY -> ENABLED_BY_ADMIN
+ Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY -> DISABLED_BY_ADMIN
+ else -> ""
+ }
+ }
+
+ @Test
+ fun blockedByAdmin_getSummaryWhenChecked() {
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+
+ val summary = blockedByAdmin.getSummary(true)
+
+ assertThat(summary).isEqualTo(ENABLED_BY_ADMIN)
+ }
+
+ @Test
+ fun blockedByAdmin_getSummaryNotWhenChecked() {
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+
+ val summary = blockedByAdmin.getSummary(false)
+
+ assertThat(summary).isEqualTo(DISABLED_BY_ADMIN)
+ }
+
+ private companion object {
+ const val RESTRICTION = "restriction"
+ val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
+ RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+
+ const val ENABLED_BY_ADMIN = "Enabled by admin"
+ const val DISABLED_BY_ADMIN = "Disabled by admin"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
index ab34f6820014..72a5bd76e737 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
@@ -105,7 +105,8 @@ class AppInfoTest {
}
}
- composeTestRule.onNodeWithText("version $VERSION_NAME").assertIsDisplayed()
+ composeTestRule.onNodeWithText(text = "version $VERSION_NAME", substring = true)
+ .assertIsDisplayed()
}
@Test
@@ -119,10 +120,10 @@ class AppInfoTest {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
- appInfoProvider.FooterAppVersion(true)
+ appInfoProvider.FooterAppVersion(showPackageName = true)
}
}
- composeTestRule.onNodeWithText(PACKAGE_NAME).assertIsDisplayed()
+ composeTestRule.onNodeWithText(text = PACKAGE_NAME, substring = true).assertIsDisplayed()
}
@@ -137,10 +138,10 @@ class AppInfoTest {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
- appInfoProvider.FooterAppVersion(false)
+ appInfoProvider.FooterAppVersion(showPackageName = false)
}
}
- composeTestRule.onNodeWithText(PACKAGE_NAME).assertDoesNotExist()
+ composeTestRule.onNodeWithText(text = PACKAGE_NAME, substring = true).assertDoesNotExist()
}
private companion object {
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
index 3b2fe0f5c16b..d158a2414f85 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
@@ -32,44 +32,37 @@ import com.android.settingslib.spaprivileged.test.R
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
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
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppOpPermissionAppListTest {
- @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+ @get:Rule
+ val composeTestRule = createComposeRule()
- @get:Rule val composeTestRule = createComposeRule()
+ private val packageManagers = mock<IPackageManagers>()
- @Spy private val context: Context = ApplicationProvider.getApplicationContext()
+ private val appOpsManager = mock<AppOpsManager>()
- @Mock private lateinit var packageManagers: IPackageManagers
-
- @Mock private lateinit var appOpsManager: AppOpsManager
-
- @Mock private lateinit var packageManager: PackageManager
-
- private lateinit var listModel: TestAppOpPermissionAppListModel
+ private val packageManager = mock<PackageManager> {
+ doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any())
+ }
- @Before
- fun setUp() {
- whenever(context.appOpsManager).thenReturn(appOpsManager)
- whenever(context.packageManager).thenReturn(packageManager)
- doNothing().whenever(packageManager)
- .updatePermissionFlags(any(), any(), any(), any(), any())
- listModel = TestAppOpPermissionAppListModel()
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { appOpsManager } doReturn appOpsManager
+ on { packageManager } doReturn packageManager
}
+ private val listModel = TestAppOpPermissionAppListModel()
+
@Test
fun transformItem_recordHasCorrectApp() {
val record = listModel.transformItem(APP)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
index 8bfae14b3b8f..270b3faa7ec6 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
@@ -38,44 +38,34 @@ import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsPro
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppInfoPageTest {
@get:Rule
val composeTestRule = createComposeRule()
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
private val context: Context = ApplicationProvider.getApplicationContext()
- @Mock
- private lateinit var packageManagers: IPackageManagers
+ private val packageManagers = mock<IPackageManagers> {
+ on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO
+ }
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
- private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+ private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply {
+ restrictedMode = NoRestricted
+ }
private val appListTemplate =
TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider))
private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate)
- @Before
- fun setUp() {
- fakeRestrictionsProvider.restrictedMode = NoRestricted
- whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID))
- .thenReturn(PACKAGE_INFO)
- }
-
@Test
fun buildEntry() {
val entryList = appInfoPageProvider.buildEntry(null)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt
new file mode 100644
index 000000000000..e450364a9ab2
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.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.settingslib.spaprivileged.template.common
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.content.pm.UserProperties
+import android.os.UserManager
+import androidx.compose.material3.Text
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class UserProfilePagerTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val mockUserManager = mock<UserManager> {
+ on { getProfiles(any()) } doReturn listOf(USER_0)
+ on { getUserProperties(USER_0.userHandle) } doReturn
+ UserProperties.Builder()
+ .setShowInSettings(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
+ .build()
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn mockUserManager
+ }
+
+ @Test
+ fun userProfilePager() {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ UserProfilePager { userGroup ->
+ Text(text = userGroup.userInfos.joinToString { it.id.toString() })
+ }
+ }
+ }
+
+ composeTestRule.onNodeWithText(USER_0.id.toString()).assertIsDisplayed()
+ }
+
+ private companion object {
+ val USER_0 = UserInfo(0, "", 0)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreferenceTest.kt
new file mode 100644
index 000000000000..55c16bd20336
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedMainSwitchPreferenceTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.spaprivileged.template.preference
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.isOff
+import androidx.compose.ui.test.isOn
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+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.FakeRestrictionsProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RestrictedMainSwitchPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+
+ private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+
+ private val switchPreferenceModel = object : SwitchPreferenceModel {
+ override val title = TITLE
+ private val checkedState = mutableStateOf(true)
+ override val checked = { checkedState.value }
+ override val onCheckedChange: (Boolean) -> Unit = { checkedState.value = it }
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_toggleable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ composeTestRule.onNode(isOff()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenNoRestricted_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenNoRestricted_toggleable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ composeTestRule.onNode(isOff()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
+ composeTestRule.onNode(isOff()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_notToggleable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ composeTestRule.onNode(isOff()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertDoesNotExist()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
+ }
+
+ private fun setContent(restrictions: Restrictions) {
+ composeTestRule.setContent {
+ RestrictedMainSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
+ fakeRestrictionsProvider
+ }
+ }
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val USER_ID = 0
+ const val RESTRICTION_KEY = "restriction_key"
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_bt_untethered_earbuds.xml b/packages/SettingsLib/res/drawable/ic_bt_untethered_earbuds.xml
new file mode 100644
index 000000000000..bcf5b91797d1
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_untethered_earbuds.xml
@@ -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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal" >
+ <path
+ android:fillColor="#4E4639"
+ android:pathData="M21.818,18.14V15.227C21.818,12.522 19.615,10.318 16.909,10.318C14.204,10.318 12,12.522 12,15.227V19.591C12,22.296 14.204,24.5 16.909,24.5C17.662,24.5 18.382,24.326 19.025,24.02C19.538,24.326 20.127,24.5 20.727,24.5C22.527,24.5 24,23.028 24,21.228C24,19.809 23.084,18.587 21.818,18.14ZM16.909,12.5C18.414,12.5 19.636,13.722 19.636,15.227C19.636,16.733 18.414,17.955 16.909,17.955C15.404,17.955 14.182,16.733 14.182,15.227C14.182,13.722 15.404,12.5 16.909,12.5ZM14.182,19.591V19.308C14.967,19.831 15.906,20.136 16.909,20.136C17.913,20.136 18.851,19.831 19.636,19.308V19.591C19.636,19.635 19.636,19.678 19.636,19.711C19.636,19.722 19.636,19.733 19.636,19.744C19.636,19.777 19.636,19.82 19.625,19.853C19.625,19.864 19.625,19.886 19.625,19.896C19.625,19.918 19.615,19.951 19.615,19.973C19.615,19.995 19.604,20.028 19.604,20.049C19.604,20.06 19.593,20.082 19.593,20.093C19.549,20.3 19.494,20.497 19.407,20.693C19.385,20.736 19.364,20.78 19.342,20.824C19.342,20.835 19.331,20.846 19.331,20.846C19.32,20.867 19.309,20.889 19.298,20.911C19.287,20.933 19.265,20.966 19.254,20.987C19.244,20.998 19.244,21.009 19.233,21.02C19.211,21.053 19.2,21.075 19.178,21.107C19.178,21.107 19.178,21.108 19.178,21.118C18.687,21.838 17.858,22.318 16.92,22.318C15.404,22.318 14.182,21.097 14.182,19.591Z" />
+ <path
+ android:fillColor="#4E4639"
+ android:pathData="M12,5.409C12,2.704 9.796,0.5 7.091,0.5C4.385,0.5 2.182,2.704 2.182,5.409V8.322C0.916,8.769 0,9.991 0,11.409C0,13.209 1.473,14.682 3.273,14.682C3.873,14.682 4.462,14.507 4.975,14.202C5.618,14.507 6.338,14.682 7.091,14.682C9.796,14.682 12,12.478 12,9.773V5.409ZM7.091,2.682C8.596,2.682 9.818,3.904 9.818,5.409C9.818,6.915 8.596,8.136 7.091,8.136C5.585,8.136 4.364,6.915 4.364,5.409C4.364,3.904 5.585,2.682 7.091,2.682ZM7.091,12.5C6.153,12.5 5.324,12.02 4.833,11.3C4.833,11.3 4.833,11.3 4.833,11.289C4.811,11.256 4.8,11.234 4.778,11.202C4.767,11.191 4.767,11.18 4.756,11.169C4.745,11.147 4.724,11.115 4.713,11.093C4.702,11.071 4.691,11.049 4.68,11.027C4.68,11.016 4.669,11.005 4.669,11.005C4.647,10.962 4.625,10.918 4.604,10.875C4.516,10.678 4.451,10.482 4.418,10.274C4.418,10.264 4.407,10.242 4.407,10.231C4.407,10.209 4.396,10.176 4.396,10.155C4.396,10.133 4.385,10.1 4.385,10.078C4.385,10.067 4.385,10.045 4.385,10.035C4.385,10.002 4.375,9.958 4.375,9.925C4.375,9.915 4.375,9.904 4.375,9.893C4.364,9.86 4.364,9.816 4.364,9.773V9.489C5.149,10.013 6.087,10.318 7.091,10.318C8.095,10.318 9.033,10.013 9.818,9.489V9.773C9.818,11.278 8.596,12.5 7.091,12.5Z" />
+</vector>
+
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 115d126c06ea..d2825209ee5f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktief, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batterykrag"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktief, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batterykrag, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batterykrag"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batterykrag"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batterykrag, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batterykrag"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Links <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Regs <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktief"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gestoor"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktief, net links"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktief, net regs"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktief, links en regs"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Aanvaar dat programme moderne formate steun"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Wys kodewisselingkennisgewings"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Deaktiveer kodewisselingkas"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-instellings"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forseer terugskakeling na L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Kies vir geforseerde terugskakeling na L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Lopende dienste"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Sien en beheer dienste wat tans aktief is"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Maak toestel wakker om hier te speel"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Toestel is nie goedgekeur om te speel nie"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Kan nie hierdie media hier speel nie"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Gekoppel"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Gekoppel deur ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Gekoppel deur eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV-verstek"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-uitset"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interne luidsprekers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 97de8d9d51b7..f047648fd905 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"መተግበሪያዎች ዘመናዊ ቅርጸቶችን እንደሚደግፉ አድርገው ይቁጠሩ"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ትራንስኮዲንግ ማሳወቂያዎችን አሳይ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"የትራንስኮዲንግ መሸጎጫን አሰናክል"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"የWidevine ቅንብሮች"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"የL3 ተመልሶ ወዳቂ ግዳጅ"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"የL3 ተመልሶ ወዳቂን ለማስገደድ ይምረጡ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"አሂድ አገልግሎቶች"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"በአሁኑጊዜ እየሄዱ ያሉ አገልግሎቶችን ተቆጣጠር እና እይ"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"የWebView ትግበራ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"እዚህ ጋር ለመጫወት መሣሪያን ያንቁ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"መሣሪያ ለማጫወት አልጸደቀም"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ይህን ሚዲያ እዚህ ጋር ማጫወት አይቻልም"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"ተገናኝቷል"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ኤአርሲ"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI ኢኤአርሲ"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"በኤአርሲ በኩል ተገናኝቷል"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"በኢኤአርሲ በኩል ተገናኝቷል"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"የቲቪ ነባሪ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"የHDMI ውጽዓት"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ውስጣዊ ድምፅ ማውጫዎች"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
<string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index b2cd5feb3897..7fa5c03e5eb4 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"نشط، ومستوى البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"مفعّلة، مستوى البطارية: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، المعدّل: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"‏مستوى شحن البطارية: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"مستوى البطارية: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، المعدّل: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"‏مستوى شحن البطارية في سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"‏مستوى شحن البطارية في سماعة الرأس اليمنى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"نشط"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"محفوظ"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"السمّاعة الطبية اليسرى فقط مفعَّلة"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"السمّاعة الطبية اليمنى فقط مفعَّلة"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"السمّاعتان اليسرى واليمنى مفعَّلتان"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"افتراض أن التطبيق يتوافق مع التنسيقات الحديثة"</string>
<string name="transcode_notification" msgid="5560515979793436168">"إظهار إشعارات تحويل الترميز"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"إيقاف ذاكرة التخزين المؤقت لميزة \"تحويل الترميز\""</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"‏إعدادات Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"‏فرض الرجوع إلى المستوى L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"‏اختيار فرض الرجوع إلى المستوى L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"الخدمات قيد التشغيل"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"عرض الخدمات قيد التشغيل في الوقت الحالي والتحكم فيها"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"‏تطبيق WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"نشِّط الجهاز للتشغيل هنا"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"غير مسموح له بتشغيل وسائط"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"هذه الوسائط غير متوافقة"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"متّصل"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"‏متّصل من خلال ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"‏متّصل من خلال eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"الجهاز التلقائي لإخراج صوت التلفزيون"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"‏إخراج الصوت من خلال HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"مكبّرات الصوت الداخلية"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
<string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 4e9326e76030..a2d6955b8950 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"সক্ৰিয়, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"সক্ৰিয়, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"বাওঁফালে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"সোঁফালে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"সক্ৰিয়"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"ছেভ কৰা হৈছে"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"কেৱল বাঁওফালৰটো সক্ৰিয় হৈছে"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"কেৱল সোঁফালৰটো সক্ৰিয় হৈছে"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"বাওঁ আৰু সোঁ দুয়োফালৰ সক্ৰিয় হৈছে"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"এপে আধুনিক ফৰ্মেট সমৰ্থন কৰে বুলি ধৰি লওক"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ট্ৰান্সক\'ডিঙৰ জাননী দেখুৱাওক"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ট্ৰান্সক\'ডিঙৰ কেশ্ব অক্ষম কৰক"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevineৰ ছেটিং"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"force L3 fallback সক্ষম কৰিবলৈ বাছনি কৰক"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"চলিত সেৱা"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"বৰ্তমান চলি থকা সেৱাসমূহ চাওক আৰু নিয়ন্ত্ৰণ কৰক"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"ৱেবভিউ প্ৰয়োগ"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ইয়াত প্লে\' কৰিবলৈ ডিভাইচটো সক্ৰিয় কৰক"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"প্লে\' কৰিবলৈ ডিভাইচটো অনুমোদিত নহয়"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ইয়াত এই মিডিয়াটো প্লে\' কৰিব নোৱাৰি"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"সংযুক্ত হৈ আছে"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARCৰ জৰিয়তে সংযুক্ত"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARCৰ জৰিয়তে সংযুক্ত"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"টিভি ডিফ’ল্ট"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI আউটপুট"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"অভ্যন্তৰীণ স্পীকাৰ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
<string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index c16d058ab5ab..99af5570b09d 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiv, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batareya, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batareya"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batareya: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batareya, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batareya"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Yadda saxlandı"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, yalnız sol"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, yalnız sağ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, sol və sağ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Tətbiqlərin müasir formatları dəstəklədiyini qəbul edin"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Kod dəyişmə bildirişlərini göstərin"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Keşin kodlaşdırılmasını deaktiv edin"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ayarları"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 alternativini məcburi tətbiq edin"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 alternativinin məcburi tətbiqi üçün seçim edin"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"İşləyən xidmətlər"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"İşlək xidmətlərə baxış və onların idarəedilməsi"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView servisi"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Burada oxutmaqçün cihazı oyat"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Cihaz oxutmaq üçün təsdiqlənməyib"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Bu medianı burada oxutmaq olmur"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Qoşulub"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI Chrome-da Tətbiqin İşləmə Müddəti"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Chrome-da Tətbiqin İşləmə Müddəti vasitəsilə qoşulub"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC vasitəsilə qoşulub"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV Defoltu"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Çıxışı"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Daxili Dinamiklər"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım və rəy"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index e326c6408811..cca83154a381 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Podrazumevaj da aplikacije podržavaju moderne formate"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Prikazuj obaveštenja o transkodiranju"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Onemogući keš transkodiranja"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine podešavanja"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Prinudno primeni L3 rezervu"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Izaberite da biste prinudno primenili L3 rezervu"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Primena WebView-a"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Probudite uređaj da biste pustili ovde"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Uređaj nije odobren za reprodukciju"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Ne možete da pustite ovaj medijski fajl ovde"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Povezano"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano preko ARC-a"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano preko eARC-a"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Podrazumevana vrednost za TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI izlaz"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Unutrašnji zvučnici"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 2458eac65f91..de703e98bf34 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Уключана, зарад <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Актыўна, Л: акумулятар: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, П: акумулятар: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Л: акумулятар: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, П: акумулятар: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Левы: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Правы: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Уключана"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Захавана"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Уключана, толькі для левага вуха"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Уключана, толькі для правага вуха"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Уключана, для левага і правага вуха"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Лічыца, што праграмы падтрымліваюць сучасныя фарматы"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Паказваць апавяшчэнні пра перакадзіраванне"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Адключыць кэш перакадзіравання"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Налады Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Прымусовы пераход на ўзровень бяспекі 3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Выберыце, каб прымусова пераходзіць на ўзровень бяспекі 3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Запушчаныя службы"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Прагляд запушчаных службаў i кіраванне iмi"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Рэалізацыя WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Для прайгравання на гэтай прыладзе абудзіце яе"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Для прайгравання на прыладзе патрабуецца ўхваленне"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Тут не ўдаецца прайграць мультымедыя"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Падключана"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Падключана праз ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Падключана праз eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Стандартны (тэлевізар)"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Выхад HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Унутраныя дынамікі"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
<string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 74641d1ade6c..fd173c1f379e 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Активно. Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активно. Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерия. Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерия"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерия. Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерия"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Вляво: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Вдясно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активно"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Запазено"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активно – само лявото"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активно – само дясното"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активно – лявото и дясното"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Предполагане, че приложенията поддържат съвременни формати"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Показване на известията за прекодиране"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Деактивиране на кеша за прекодиране"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Настройки за Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Налагане на резервния вариант за L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Избиране на налагане на резервния вариант за L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Изпълнявани услуги"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Преглед и контрол върху изпълняващите се понастоящем услуги"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Внедряване на WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Активирайте устройството, за да възпроизведете съдържанието тук"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Устройството не е одобрено да възпроизвежда съдържание"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Мултимедийното съдържание не може да се възпроизведе тук"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Установена е връзка"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Свързано посредством ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Свързано посредством eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Стандартна настройка за телевизора"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI изход"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Вградени високоговорители"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string>
<string name="help_label" msgid="3528360748637781274">"Помощ и отзиви"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c102722d540f..2290c09a5f79 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"চালু আছে, চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"চালু, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ব্যাটারি, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ব্যাটারি"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ব্যাটারি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ব্যাটারি, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ব্যাটারি"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"বাঁদিকের হেডসেটে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বাকি আছে"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"ডানদিকের হেডসেটে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বাকি আছে"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"চালু আছে"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"সেভ করা আছে"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"শুধুমাত্র বাঁদিকের হিয়ারিং এড অ্যাক্টিভ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"শুধুমাত্র ডানদিকের হিয়ারিং এড অ্যাক্টিভ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"বাঁ ও ডানদিকের হিয়ারিং এড, অ্যাক্টিভ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"অ্যাপ মর্ডার্ন ফর্ম্যাটে কাজ করবে বলে ধরে নিন"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ট্রান্সকোডিং বিজ্ঞপ্তি দেখুন"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ক্যাশে ট্রান্সকোডিং বন্ধ করুন"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"ওয়াইডভাইন সেটিংস"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"ফোর্স L3 ফলব্যাক"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ফোর্স L3 ফলব্যাক চালু করবেন কিনা তা বেছে নিন"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"এখন চলছে যে পরিষেবাগুলি"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"বর্তমান চলমান পরিষেবাগুলি দেখুন এবং নিয়ন্ত্রণ করুন"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"ওয়েবভিউ প্রয়োগ"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"এখানে চালানোর জন্য ডিভাইসকে জাগান"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"চালানোর জন্য ডিভাইসের অনুমতি নেই"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"এখানে এই মিডিয়া চালানো যাচ্ছে না"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"কানেক্ট করা হয়েছে"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"টিভির ডিফল্ট অডিও আউটপুট"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI আউটপুট"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ইন্টার্নাল স্পিকার"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
<string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index c4ef89b80684..70576195f4b6 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -99,10 +99,10 @@
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
<string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Lijevo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Desno <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Lijeva <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Desna <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktivan"</string>
- <string name="bluetooth_saved_device" msgid="4895871321722311428">"Spremljeno"</string>
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Sačuvano"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo lijevi"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, samo desni"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, lijevi i desni"</string>
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Pretpostavi da aplikacije podržavaju moderne formate"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Prikaži obavještenja o transkodiranju"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Onemogućite keš memoriju za transkodiranje"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Postavke za Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Nametnite zamjenu L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Odaberite da nametnete zamjenu L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Postavljanje WebViewa"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktivirajte uređaj da reproducirate ovdje"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Uređaj nije odobren za reprodukciju"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Nije moguće ovdje reproducirati medij"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Povezano"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano je putem ARC-a"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano je putem eARC-a"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV je zadan"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI izlaz"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interni zvučnici"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5d1b46d14387..00c31dc31ba3 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -298,7 +298,7 @@
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Activa el còdec d\'àudio per Bluetooth\nSelecció: mode de canal"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Còdec LDAC d\'àudio de Bluetooth: qualitat de reproducció"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Activa l\'LDAC d\'àudio de Bluetooth\nSelecció de còdec: qualitat de reproducció"</string>
- <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Reproducció en continu: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
+ <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Reproducció en línia: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS privat"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Selecciona el mode de DNS privat"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"Desactivat"</string>
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Assumeix que les aplicacions són compatibles amb formats moderns"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostra les notificacions de transcodificació"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Desactiva la memòria cau per a la transcodificació"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Configuració de Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Força l\'alternativa L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecciona per forçar l\'alternativa L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Serveis en execució"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualitza i controla els serveis en execució"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementació de WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activa el dispositiu per reproduir aquí"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"El dispositiu no està aprovat per reproduir contingut"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"No es pot reproduir aquest contingut multimèdia aquí"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"S\'ha connectat"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connectat mitjançant ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connectat mitjançant eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Valor predeterminat per al televisor"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Sortida HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Altaveus interns"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 91aef964cf20..47ed7e9334f9 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Předpokládat, že aplikace podporují moderní formáty"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Zobrazit oznámení o překódování"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Deaktivovat mezipaměť pro překódování"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Nastavení Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Vynucení L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Vybrat vynucení L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Spuštěné služby"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Umožňuje zobrazit a ovládat aktuálně spuštěné služby"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementace WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Zařízení je třeba probudit"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Není schváleno k přehrávání"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Tato média zde přehrát nelze"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Připojeno"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Připojeno přes ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Připojeno přes eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Výchozí nastavení televize"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Výstup HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interní reproduktory"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
<string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1cbe27c21855..3dc39430fd21 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivt, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batteri: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri. Højre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Højre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gemt"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, kun venstre"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, kun højre"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, venstre og højre"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Gå ud fra, at apps understøtter moderne formater"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Vis notifikationer for omkodning"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Deaktiver omkodningscache"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-indstillinger"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Fremtving L3-alternativ"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Vælg for at fremtvinge L3-alternativ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Kørende tjenester"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Vis og administrer kørende tjenester"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Væk enheden for at afspille her"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Enheden er ikke godkendt"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Mediet kan ikke afspilles her"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Forbundet"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Forbundet via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Forbundet via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Fjernsyn som standardoutput"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-udgang"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interne højttalere"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index caeed8cf18ca..4a7f786fc664 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiv, Akkustand L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Akku – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Akkustand L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Links – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Rechts – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gespeichert"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, nur links"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, nur rechts"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, links und rechts"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Voraussetzen, dass Apps moderne Formate unterstützen"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Benachrichtigungen zur Transcodierung anzeigen"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Cache für Transcodierung deaktivieren"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-Einstellungen"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3-Fallback erzwingen"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Auswählen, um L3-Fallback zu erzwingen"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Gerät aktivieren, um hier etwas wiedergeben zu lassen"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Gerät nicht für die Wiedergabe zugelassen"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Diese Medien können hier nicht wiedergegeben werden"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Verbunden"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Per ARC verbunden"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Per eARC verbunden"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Standardeinstellung: Fernseher"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-Ausgang"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interne Lautsprecher"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus &amp; und wieder ein."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
<string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index a70e9b89bafd..532e380da272 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Να θεωρείται ότι οι εφαρμογές χρησιμοποιούν σύγχρονες μορφές"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Εμφάνιση ειδοποιήσεων διακωδικοποίησης"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Απενεργοποίηση κρυφής μνήμης για διακωδικοποίηση"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Ρυθμίσεις Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Επιβολή εναλλακτικής L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Επιλογή για επιβολή εναλλακτικής L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Υπηρεσίες που εκτελούνται"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Προβολή και έλεγχος των εφαρμογών που εκτελούνται αυτή τη στιγμή"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Υλοποίηση WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Αφύπνιση συσκευής για αναπαραγωγή εδώ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Η συσκευή δεν έχει εγκριθεί για αναπαραγωγή"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Δεν είναι δυνατή η αναπαραγωγή αυτού του πολυμέσου εδώ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Συνδέθηκε"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Συνδέθηκε μέσω ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Συνδέθηκε μέσω eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Προεπιλογή τηλεόρασης"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Έξοδος HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Εσωτερικά ηχεία"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Πρόβλημα κατά τη σύνδεση. Απενεργοποιήστε τη συσκευή και ενεργοποιήστε την ξανά"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ενσύρματη συσκευή ήχου"</string>
<string name="help_label" msgid="3528360748637781274">"Βοήθεια και σχόλια"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 04fa67578949..27f2fc524d08 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Active, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Active, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Active"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saved"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Disable transcoding cache"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine settings"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Select to force L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wake up device to play here"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Device not approved to play"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Can\'t play this media here"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connected"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV default"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Output"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Internal speakers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 364820d630fd..b59f76593aaa 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -562,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wake up device to play here"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Device not approved to play"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Cant play this media here"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connected"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV Default"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Output"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Internal Speakers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off &amp; back on"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 04fa67578949..27f2fc524d08 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Active, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Active, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Active"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saved"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Disable transcoding cache"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine settings"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Select to force L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wake up device to play here"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Device not approved to play"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Can\'t play this media here"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connected"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV default"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Output"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Internal speakers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 04fa67578949..27f2fc524d08 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Active, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Active, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Active"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saved"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Disable transcoding cache"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine settings"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Select to force L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wake up device to play here"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Device not approved to play"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Can\'t play this media here"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connected"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV default"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Output"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Internal speakers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 7215550c6a38..15946f4a809a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -562,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‎Wake up device to play here‎‏‎‎‏‎"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‎Device not approved to play‎‏‎‎‏‎"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎Cant play this media here‎‏‎‎‏‎"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎Connected‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎HDMI ARC‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎HDMI eARC‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎Connected via ARC‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎Connected via eARC‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎TV Default‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎HDMI Output‎‏‎‎‏‎"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎Internal Speakers‎‏‎‎‏‎"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‎Problem connecting. Turn device off &amp; back on‎‏‎‎‏‎"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎Wired audio device‎‏‎‎‏‎"</string>
<string name="help_label" msgid="3528360748637781274">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎Help &amp; feedback‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 5ceffab802f5..a76cf867760e 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Suponer que las apps admiten formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificaciones de transcodificación"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Inhabilitar caché de transcodificación"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Parámetros de configuración de Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forzar resguardo de nivel 3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Seleccionar para forzar un resguardo de nivel 3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"En ejecución"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar servicios actuales en ejecución"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activa el dispositivo para reproducir aquí"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"El dispositivo no está aprobado para reproducir"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"No se puede reproducir el contenido multimedia aquí"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Se estableció conexión con un cable ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Se estableció conexión con un cable eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Configuración predeterminada de la TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Salida de HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Bocinas internas"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a6e8a50474cc..90476b6ff396 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Activo, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Activo L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batería <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Izquierda <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Derecha <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activo"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Guardado"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo, solo oído izquierdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo, solo oído derecho"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activo, oídos izquierdo y derecho"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Considerar que las aplicaciones admiten formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificaciones de transcodificación"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Inhabilitar almacenamiento en caché para transcodificaciones"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Ajustes de Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forzar respaldo a L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecciona para forzar el respaldo a L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Servicios en ejecución"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Consulta y controla los servicios en ejecución"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activa el dispositivo para reproducir contenido aquí"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Dispositivo no aprobado para reproducir contenido"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"No se puede reproducir el contenido multimedia aquí"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Predeterminado de la televisión"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Salida HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Altavoces internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index ca2e9feedeb7..a2cafee03880 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiivne, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> akut"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiivne, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> akut, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> akut"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> akut"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Akutase: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> akut, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> akut"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Vasakpoolne: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Parempoolne: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiivne"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Salvestatud"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiivne, ainult vasak"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiivne, ainult parem"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiivne, vasak ja parem"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Oleta, et rakendused toetavad kaasaegseid vorminguid"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Kuva transkodeerimise märguanded"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Transkodeerimise vahemälu keelamine"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine\'i seaded"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 varuvariandiks sundimine"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Valige, kas sundida L3 varuvariandiks"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Käitatud teenused"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Praegu käitatud teenuste vaatamine ja juhtimine"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView\' rakendamine"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Äratage seade siin esitamiseks"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Seade ei ole esitamiseks heaks kiidetud"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Seda meediat ei saa siin esitada"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Ühendatud"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ühendatud ARC-i kaudu"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ühendatud eARC-i kaudu"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Teleri vaikeseade"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-väljund"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Sisemised kõlarid"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
<string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 7abd4b0c0371..65a88d3b741e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -562,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktibatu gailua hemen erreproduzitzeko"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Gailua ez dago erreproduzitzeko onartuta"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Ezin da erreproduzitu multimedia-eduki hau hemen"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Konektatuta"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC bidez konektatuta"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC bidez konektatuta"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Telebistaren audio-irteera lehenetsia"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI irteera"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Barneko bozgorailuak"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
<string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 97d048f53e4d..b01bcbcfe852 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"فعال، <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> شارژ باتری"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"فعال، چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> باتری، راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> باتری"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> شارژ باتری"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> باتری، راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> باتری"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"چپ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"راست <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"فعال"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"ذخیره‌شده"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"فعال، فقط چپ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"فعال، فقط راست"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"فعال، چپ و راست"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"فرض شود برنامه‌ها از قالب‌های مدرن پشتیبانی می‌کنند"</string>
<string name="transcode_notification" msgid="5560515979793436168">"نمایش اعلان‌های تراتبدیل"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"غیرفعال کردن حافظه پنهان تراتبدیل"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"‏تنظیمات Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"‏اجرای اجباری «بازگشت به L3»"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"‏برای اجرای اجباری «بازگشت به L3»، انتخاب کنید"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"سرویس‌های در حال اجرا"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"مشاهده و کنترل سرویس‌های در حال اجرای فعلی"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"اجرای وب‌نما"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"برای پخش در اینجا، دستگاه را بیدار کنید"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"دستگاه برای پخش تأیید نشده است"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"نمی‌توان این رسانه را اینجا پخش کرد"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"متصل"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"‏متصل ازطریق ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"‏متصل ازطریق eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"پیش‌فرض تلویزیون"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"‏خروجی HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"بلندگوهای داخلی"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
<string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 0adfc3af311a..58e87a6820b0 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiivinen, akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiivinen, V: akku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, O: akku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Akku (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"V: akku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, O: akku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Vasen <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Oikea <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiivinen"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Tallennettu"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiivinen, vain vasen"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiivinen, vain oikea"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiivinen, vasen ja oikea"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Oleta, että sovellukset tukevat nykyaikaisia formaatteja"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Näytä transkoodausilmoituksia"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Poista välimuistin transkoodaus käytöstä"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-asetukset"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Pakota L3-varavaihtoehto"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Pakota L3-varavaihtoehto valitsemalla tämä"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Tarkastele ja hallitse käynnissä olevia palveluita"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-käyttöönotto"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktivoi laite, jotta voit toistaa sillä"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Laitetta ei ole hyväksytty toistoa varten"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Mediaa ei voi toistaa tällä"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Yhdistetty"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Yhdistetty ARC:n kautta"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Yhdistetty eARC:n kautta"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV:n oletusasetus"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-toisto"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Sisäiset kaiuttimet"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
<string name="help_label" msgid="3528360748637781274">"Ohje ja palaute"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index de627399a78a..c889d3da305d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Actif, pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Actif, G : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; D : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"G : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; D : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Droite : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Actif"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Enregistré"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actif, gauche seulement"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, droite seulement"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, gauche et droite"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Présumer que les applications prennent en charge les formats modernes"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Afficher les notifications de transcodage"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Désactiver le cache de transcodage"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Paramètres de Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forcer le traitement de secours L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Sélectionnez pour forcer le traitement de secours L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activez l\'appareil pour faire jouer le contenu ici"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"L\'appareil n\'est pas autorisé à faire jouer le contenu"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Impossible de faire jouer ce contenu multimédia ici"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connecté"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connecté par ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connecté par eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Sortie audio par défaut de la télévision"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Sortie HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Haut-parleurs internes"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index fea9bdd49f78..8a9bedfca062 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Actif, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Actif, G : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de la batterie, D : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de la batterie"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batterie (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"G : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de la batterie, D : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de la batterie"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Gauche (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Droit (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Actif"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Enregistré"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actif, gauche uniquement"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Actif, droit uniquement"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Actifs, gauche et droit"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Supposer que les applications sont compatibles avec les formats modernes"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Afficher les notifications de transcodage"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Désactiver la cache de transcodage"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Paramètres Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forcer le retour en arrière vers le niveau 3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Sélectionner pour forcer le retour en arrière vers le niveau 3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activez l\'appareil pour y lire du contenu"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Appareil non autorisé à lire du contenu"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Impossible de lire ce contenu multimédia ici"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connecté"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connecté via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connecté via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Sortie audio par défaut de la TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Sortie HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Haut-parleurs internes"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 62bdf0995519..3258db097468 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Dispositivo activo, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Activado. E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batería: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Lado dereito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activo"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gardado"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo (só o esquerdo)"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo (só o dereito)"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activos (o esquerdo e o dereito)"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Considerar que as aplicacións admiten formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificacións de transcodificación"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Desactivar memoria caché para a transcodificación"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Configuración de Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forzar alternativa a L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Seleccionar para forzar a alternativa a L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Servizos en uso"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Comproba e controla os servizos actualmente en uso"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activa o dispositivo para reproducir o contido aquí"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"O dispositivo non está autorizado para reproducir contido"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Non se pode reproducir este produto multimedia aquí"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Saída predeterminada da televisión"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Saída HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Altofalantes internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 6b422fa93141..0dd9bfef3f95 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ધારો કે ઍપ આધુનિક ફૉર્મેટ પર કામ કરે છે"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ફૉર્મેટ બદલવાની પ્રક્રિયાના નોટિફિકેશન બતાવો"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ફૉર્મેટ બદલવાની પ્રક્રિયાની કૅશ મેમરી બંધ કરો"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevineના સેટિંગ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 ફૉલબૅકને ફરજ પાડો"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 ફૉલબૅકને ફરજ પાડવા માટે પસંદ કરો"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"અહીં ચલાવવા માટે ડિવાઇસને સક્રિય કરો"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ડિવાઇસ દ્વારા ચલાવવાની મંજૂરી આપવામાં આવી નથી"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"અહીં આ મીડિયા ચલાવી શકતા નથી"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"કનેક્ટેડ છે"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC મારફતે કનેક્ટેડ"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC મારફતે કનેક્ટેડ"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ટીવીમાંથી ડિફૉલ્ટ ઑડિયો આઉટપુટ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI આઉટપુટ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"આંતરિક સ્પીકર"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
<string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 482c9e964646..ea5c5ba9b0ab 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"मानकर चलें कि ऐप्लिकेशन, नए फ़ॉर्मैट के साथ काम करेंगे"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ट्रांसकोडिंग की सूचनाएं दिखाएं"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"कैश को ट्रांसकोड करने की सुविधा बंद करें"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine की सेटिंग"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 फ़ॉलबैक लागू करें"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"कॉन्टेंट पर L3 फ़ॉलबैक लागू करने की सेटिंग चुनें"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"चालू सेवाएं"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"इस समय चल रही सेवाओं को देखें और कंट्रोल करें"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"वेबव्यू लागू करें"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"मीडिया को यहां चलाने के लिए, डिवाइस को अनलॉक करें"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"डिवाइस को मीडिया चलाने की अनुमति नहीं दी गई है"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"इस मीडिया को यहां नहीं चलाया जा सकता"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"कनेक्ट किया गया"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"एचडीएमआई ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"एचडीएमआई eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC से कनेक्ट किए गए डिवाइस"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC से कनेक्ट किए गए डिवाइस"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"टीवी का डिफ़ॉल्ट ऑडियो आउटपुट"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"एचडीएमआई आउटपुट"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"इंटरनल स्पीकर"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string>
<string name="help_label" msgid="3528360748637781274">"सहायता और सुझाव"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index b433748f510a..346b602176b5 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Pretpostavi da aplikacije podržavaju moderne formate"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Prikaži obavijesti o konvertiranju"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Onemogući predmemoriju za konvertiranje"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Postavke Widevinea"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Prisilna L3 zamjena"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Odaberite za prisilnu L3 zamjenu"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Pregledajte i kontrolirajte trenutačno pokrenute usluge"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacija WebViewa"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktivirajte i reproducirajte ovdje"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Nije odobreno za reprodukciju"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Nemoguća je reprodukcija medija"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Povezano"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano putem ARC-a"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano putem eARC-a"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Zadano za TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI izlaz"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Unutarnji zvučnici"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 7c93b2fefefc..186b42b3fe9f 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Annak feltételezése, hogy az alkalmazások támogatják a modern formátumokat"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Átkódolási értesítések megjelenítése"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Átkódolási gyorsítótár kikapcsolása"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-beállítások"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 fallback kényszerítése"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Jelölje ki az L3 fallback kényszerítéséhez"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Futó szolgáltatások"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"A jelenleg futó szolgáltatások megtekintése és vezérlése"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-megvalósítás"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Lejátszáshoz ébressze fel az eszközt"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Az eszköz nem játszhat le"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"A tartalom nem játszható le itt"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Csatlakozva"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Csatlakoztatva ARC-n keresztül"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Csatlakoztatva eARC-n keresztül"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Alapértelmezett tévé"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-kimenet"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Belső hangszóró"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
<string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 1a189b142903..ff42a37f0be6 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Ակտիվ է։ Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Ակտիվ է, Ա` Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ձ՝ Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ա՝ Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ձ՝ Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Ձախը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Աջը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Ակտիվ է"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Պահված է"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ակտիվ, միայն ձախ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ակտիվ, միայն աջ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ակտիվ, ձախ և աջ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Ենթադրել, որ հավելվածներն աջակցում են ժամանակակից ձևաչափեր"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Ցույց տալ տրանսկոդավորման մասին ծանուցումները"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Անջատել տրանսկոդավորման քեշը"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-ի կարգավորումներ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 պահուստային տարբերակի ստիպողական գործարկում"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Ընտրեք՝ L3 պահուստային տարբերակը ստիպողաբար գործարկելու համար"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Աշխատող ծառայություններ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Դիտել և վերահսկել ընթացիկ աշխատող ծառայությունները"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ծառայություն"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Արթնացրեք սարքը՝ այստեղ նվագարկելու համար"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Նվագարկելու համար հաստատեք սարքը"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Չհաջողվեց նվագարկել մեդիա ֆայլն այստեղ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Միացված է"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Միացված է ARC-ի միջոցով"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Միացված է eARC-ի միջոցով"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Հեռուստացույցի կանխադրված կարգավորումներ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ելք"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Ներքին բարձրախոսներ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
<string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index cd8ab5a42a81..7159d870d37b 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktif, baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktif, Kr: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterai, Kn: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterai"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Kr: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterai, Kn: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterai"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Kiri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktif"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Disimpan"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktif, hanya kiri"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktif, hanya kanan"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktif, kiri dan kanan"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Asumsikan aplikasi mendukung format modern"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Tampilkan notifikasi transcoding"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Nonaktifkan cache transcoding"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Setelan Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Paksa penggantian L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Pilih untuk memaksa penggantian L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Layanan yang sedang berjalan"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Lihat dan kontrol layanan yang sedang berjalan"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Penerapan WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Mengaktifkan perangkat untuk memutar di sini"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Perangkat tidak disetujui untuk memutar"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Tidak dapat memutar media ini di sini"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Terhubung"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Terhubung melalui ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Terhubung melalui eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV sebagai Default"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Output HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Speaker Internal"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat &amp; aktifkan kembali"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan &amp; masukan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 562152619c32..53f904523042 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Tengt, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Virkt, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> rafhlaða, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlaða"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Rafhlaða <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> rafhlaða, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlaða"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Vinstri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Hægri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Virkt"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Vistað"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Virkt, aðeins vinstra"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Virkt, aðeins hægra"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Virkt, vinstra og hægra"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Gera ráð fyrir að forrit styðji nútímasnið"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Sýna umkóðunartilkynningar"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Slökkva á skyndiminni umkóðunar"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Stillingar Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Þvinga fram L3 varaleið"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Veldu til að þvinga fram L3 varaleið"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Þjónustur í gangi"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Skoða og stjórna þjónustum í gangi"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Innleiðing WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Vektu tæki til að spila hér"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Tæki er ekki samþykkt til spilunar"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Ekki er hægt að spila þetta efni hér"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Tengt"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Tengt með ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Tengt með eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Sjálfgefið í sjónvarpi"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-úttak"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Innri hátalarar"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
<string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 9079750a83c9..f83594df1948 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Attivo - Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Attivo, S: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"S: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Sinistra: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Destra: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Attivo"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Dispositivo salvato"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Attiva, solo sinistra"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Attiva, solo destra"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Attivo, destra e sinistra"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Presupponi che le app supportino i formati moderni"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostra notifiche relative alla transcodifica"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Disattiva memorizzazione nella cache per la transcodifica"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Impostazioni Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forza riserva L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Seleziona per forzare riserva L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Servizi in esecuzione"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizza e controlla i servizi attualmente in esecuzione"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementazione di WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Riattiva il dispositivo per riprodurre qui"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Dispositivo non approvato per la riproduzione"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Impossibile riprodurre questo contenuto multimediale qui"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Connessione eseguita"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"ARC HDMI"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"eARC HDMI"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connessione tramite ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connessione tramite eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Valore predefinito: TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Uscita HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Speaker interni"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
<string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 296ee5a9d678..755e8ce64397 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"פעיל, טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"פעיל, שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> סוללה, ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> סוללה"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"סוללה <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> סוללה, ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> סוללה"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> שמאלי"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ימני"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"פעיל"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"בוצעה שמירה"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"פועל: שמאל בלבד"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"פועל: ימין בלבד"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"פועל: ימין ושמאל"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"הנחת העבודה היא שאפליקציות תומכות בפורמטים מודרניים"</string>
<string name="transcode_notification" msgid="5560515979793436168">"הצגת התראות לגבי המרת קידוד"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"השבתת השמירה של המרת הקידוד במטמון"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"‏הגדרות Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"‏חלופה ל-Force L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"‏בחירה להפעלת חלופה ל-Force L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"‏יישום WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"צריך להעיר את המכשיר"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"המכשיר לא אושר"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"אי אפשר להפעיל מדיה"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"יש חיבור"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"‏חיבור דרך ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"‏חיבור דרך eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ברירת המחדל של הטלוויזיה"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"‏פלט HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"רמקולים פנימיים"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
<string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 547da2c5b654..d9f76fd46925 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"アプリによる最新形式のサポートを想定"</string>
<string name="transcode_notification" msgid="5560515979793436168">"コード変換に関する通知の表示"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"コード変換のキャッシュを無効にする"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine の設定"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 代替の強制"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 代替を強制するかどうか選択します"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"実行中のサービス"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"現在実行中のサービスを表示して制御する"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView の実装"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"デバイスの起動が必要です"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"再生が許可されていません"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"このメディアは再生できません"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"接続済み"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC 経由で接続済み"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC 経由で接続済み"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"テレビのデフォルト"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI 出力"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"内蔵スピーカー"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string>
<string name="help_label" msgid="3528360748637781274">"ヘルプとフィードバック"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 3fb92746c79e..fb58973a172e 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"დაშვება, რომ აპებს აქვთ თანამედროვე ფორმატების მხარდაჭერა"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ტრანსკოდირების შეტყობინებების ჩვენება"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ტრანსკოდირების ქეშის გათიშვა"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine პარამეტრები"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"აირჩიეთ force L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"მიმდინარე სერვისები"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ამჟამად მოქმედი სერვისების ნახვა და მართვა"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView რეალიზაცია"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"დასაკრავად გამოაღვიძეთ ტელეფონი"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"მოწყობილობა არ არის ავტორიზებული დასაკრავად"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ამ მედიის აქ დაკვრა შეუძლებელია"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"დაკავშირებული"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"დაკავშირებულია ARC-ის მეშვეობით"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"დაკავშირებულია eARC-ის მეშვეობით"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ნაგულისხმევი ტელევიზორი"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"გამომავალი HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"შიდა დინამიკები"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
<string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 4753d8131b77..c99113355363 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Қосулы, батарея қуаты: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Қосулы, С: батарея заряды – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, О: батарея заряды – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Батарея қуаты: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батарея: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"С: батарея заряды – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, О: батарея заряды – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Сол: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Оң: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Қосулы"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сақталған"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Тек сол жағы қосулы"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Тек оң жағы қосулы"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Екеуі де қосулы"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Қолданбалар қазіргі заманғы форматтарды қолдайды делік"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Қайта кодтау хабарландыруларын көрсету"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Қайта кодтау кэшін өшіру"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine параметрлері"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 баламасын мәжбүрлі түрде пайдалану"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 баламасын мәжбүрлі түрде пайдалануды таңдайсыз."</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Қосылып тұрған қызметтер"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Қазір істеп тұрған қызметтерді көру және басқару"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView қызметі"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Осы жерде ойнату үшін құрылғыны ұйқы режимінен шығарыңыз."</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Ойнату үшін авторизация керек."</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Бұл мультимедиа файлын осы жерде ойнату мүмкін емес."</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Жалғанған."</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC арқылы жалғанған."</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC арқылы жалғанған."</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Әдепкі теледидар"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI шығыс құрылғысы"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Ішкі динамиктер"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
<string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2033319b0658..7ad4e53b6eaf 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"សន្មតថាកម្មវិធី​អាចប្រើ​ទម្រង់ទំនើបបាន"</string>
<string name="transcode_notification" msgid="5560515979793436168">"បង្ហាញការជូនដំណឹង​អំពីការបំប្លែងកូដ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"បិទ​ឃ្លាំងបម្រុង​សម្រាប់​ការបំប្លែងកូដ"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"ការកំណត់ Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"បង្ខំជម្រើសជំនួស L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ជ្រើសរើសដើម្បីបង្ខំជម្រើសជំនួស L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"សេវាកម្ម​កំពុង​ដំណើរការ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"មើល និង​គ្រប់គ្រង​សេវាកម្ម​កំពុង​ដំណើរការ​បច្ចុប្បន្ន"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"ការអនុវត្ត WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ដាស់ឧបករណ៍ឱ្យចាក់នៅទីនេះ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ឧបករណ៍មិន​ព្រមឱ្យចាក់ទេ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"មិនអាចចាក់មេឌៀនេះ​នៅទីនេះបានទេ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"បានភ្ជាប់"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"បានភ្ជាប់តាមរយៈ ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"បានភ្ជាប់តាមរយៈ eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"លំនាំដើម​ទូរទស្សន៍"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"ធាតុចេញ HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ឧបករណ៍បំពងសំឡេងខាងក្នុង"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មាន​បញ្ហា​ក្នុងការ​ភ្ជាប់។ បិទ រួច​បើក​ឧបករណ៍​វិញ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍​សំឡេងប្រើខ្សែ"</string>
<string name="help_label" msgid="3528360748637781274">"ជំនួយ និងមតិកែលម្អ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 71b3e6ed1352..7b71639380c7 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ಆ್ಯಪ್‌ಗಳು ಆಧುನಿಕ ಫಾರ್ಮ್ಯಾಟ್‌ಗಳನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ ಎಂದು ಊಹಿಸಿ"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸಿ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ಟ್ರಾನ್ಸ್‌ಕೋಡಿಂಗ್ ಕ್ಯಾಷ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 ಫಾಲ್‌ಬ್ಯಾಕ್"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Force L3 ಫಾಲ್‌ಬ್ಯಾಕ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ರನ್‌ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳು"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ಈಗ ರನ್‌ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಹೊಂದಿಸಿ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧನವನ್ನು ಎಚ್ಚರಿಸಿ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ಪ್ಲೇ ಮಾಡಲು ಸಾಧನವನ್ನು ಅನುಮೋದಿಸಲಾಗಿಲ್ಲ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ಈ ಮಾಧ್ಯಮವನ್ನು ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ಟಿವಿ ಡೀಫಾಲ್ಟ್"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ಔಟ್‌‌ಪುಟ್"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ಆಂತರಿಕ ಸ್ಪೀಕರ್‌ಗಳು"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
<string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index ae1822b24ef8..b2564a0150d2 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"활성, 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"활성, 왼쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"왼쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"오른쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"활성"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"저장됨"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"활성, 왼쪽만"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"활성, 오른쪽만"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"활성, 왼쪽 및 오른쪽"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"앱이 최신 형식을 지원하는 것으로 가정"</string>
<string name="transcode_notification" msgid="5560515979793436168">"트랜스코딩 알림 표시"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"트랜스코딩 캐시 사용 중지"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine 설정"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 대체 강제"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 대체를 강제하려면 선택하세요."</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"실행 중인 서비스"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"현재 실행 중인 서비스 보기 및 제어"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 구현"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"기기 절전 모드 해제 후 여기에서 재생"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"기기에서 재생을 승인하지 않음"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"여기에서 이 미디어를 재생할 수 없음"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"연결됨"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC를 통해 연결됨"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC를 통해 연결됨"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV 기본값"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI 출력"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"내부 스피커"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string>
<string name="help_label" msgid="3528360748637781274">"고객센터"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 956b27248ace..18f792c56cd7 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Иштеп жатат, батарея: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активдүү, сол: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, оң: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батареянын кубаты: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Сол: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, оң: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Сол: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Оң: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Жигердүү"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сакталган"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Иштеп жатат, сол кулак гана"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Жигердүү, оң кулакчын гана"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Жигердүү, сол жана оң кулакчын"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Колдонмолордо заманбап форматтар колдоого алынат"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Транскоддоо билдирмелерин көрсөтүү"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Транскоддоо кешин өчүрүү"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine параметрлери"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 деңгээлин мажбурлап артка кайтаруу"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 деңгээлин мажбурлап артка кайтаруу үчүн тандаңыз"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView кызматы"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Угуу үчүн түзмөктү уйкудан чыгарыңыз"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Ойнотуу үчүн уруксат алышыңыз керек"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Медиа файлды ойното албайсыз"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Туташты"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC аркылуу туташты"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC аркылуу туташты"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV\'нин демейки параметрлери"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Аудио түзмөгү"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Ички динамиктер"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
<string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index c716e5f2de6e..c75326e90043 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"ເປີດໃຊ້ຢູ່, ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"ເປີດໃຊ້, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ແບັດເຕີຣີ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ແບັດເຕີຣີ"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ແບັດເຕີຣີ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ແບັດເຕີຣີ"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"ຊ້າຍ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"ຂວາ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ອອນລາຍ"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"ບັນທຶກແລ້ວ"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ນຳໃຊ້ຢູ່, ຊ້າຍເທົ່ານັ້ນ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ນຳໃຊ້ຢູ່, ຂວາເທົ່ານັ້ນ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ນຳໃຊ້ຢູ່, ຊ້າຍ ແລະ ຂວາ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ສົມມຸດວ່າແອັບຮອງຮັບຮູບແບບສະໄໝໃໝ່"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ສະແດງການແຈ້ງເຕືອນການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ປິດການນຳໃຊ້ແຄສການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດ"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"ການຕັ້ງຄ່າ Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"ບັງຄັບທາງເລືອກ L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ເລືອກເພື່ອບັງຄັບທາງເລືອກ L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະ ຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ປຸກອຸປະກອນເພື່ອຫຼິ້ນຢູ່ບ່ອນນີ້"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ອຸປະກອນບໍ່ອະນຸມັດໃຫ້ຫຼິ້ນ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ຫຼິ້ນມີເດຍນີ້ຢູ່ບ່ອນນີ້ບໍ່ໄດ້"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ເຊື່ອມຕໍ່ຜ່ານ ARC ແລ້ວ"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"ເຊື່ອມຕໍ່ຜ່ານ eARC ແລ້ວ"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ຄ່າເລີ່ມຕົ້ນສຳລັບໂທລະທັດ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"ເອົ້າພຸດ HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ລຳໂພງຂອງເຄື່ອງ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
<string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e7ad35959cbc..37a35769f361 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktyvus, akumuliatoriaus įkrova: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktyvi, KAIRĖ: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, DEŠINĖ: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Akumuliatoriaus įkrova: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"KAIRĖ: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, DEŠINĖ: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Kairė: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Dešinė: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktyvus"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Išsaugota"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktyvus, tik kairysis"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktyvus, tik dešinysis"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktyvus, kairysis ir dešinysis"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Manoma, kad programos palaiko modernius formatus"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Rodyti perkodavimo pranešimus"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Išjungti talpyklos perkodavimą"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"„Widevine“ nustatymai"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Priverstinai naudoti L3 kaip atsarginį variantą"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Pasirinkite, kad priverstinai naudoti L3 kaip atsarginį variantą"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Vykdomos paslaugos"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Žiūrėti ir valdyti dabar vykdomas paslaugas"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"„WebView“ diegimas"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Pažadinkite įrenginį, kad galėtumėte čia leisti"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Įrenginys nepatvirtintas, kad būtų galima leisti"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Čia negalima leisti šio medijos failo"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Prisijungta"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI „eARC“"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Prisijungta per ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Prisijungta per „eARC“"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV numatytasis"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI išvesti"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Vidiniai garsiakalbiai"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
<string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 4c21e1e9b385..6cd9c577a2ee 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktīvs, akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktīvs, L: akumulatora uzlādes līmenis <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: akumulatora uzlādes līmenis <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: akumulatora uzlādes līmenis <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: akumulatora uzlādes līmenis <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Kreisā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Labā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktīvs"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saglabāta"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ierīce aktīva, tikai kreisā auss"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ierīce aktīva, tikai labā auss"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ierīces aktīvas, kreisā un labā auss"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Pieņemt, ka lietotnēs tiek atbalstīti moderni formāti"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Rādīt paziņojumus par pārkodēšanu"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Atspējot saglabāšanu kešatmiņā pārkodēšanai"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine iestatījumi"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Aktivizēt L3 atkāpšanos"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Atlasiet, lai aktivizētu L3 atkāpšanos"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Aktīvie pakalpojumi"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Pašreiz darbojošos pakalpojumu skatīšana un vadība"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ieviešana"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Lai atskaņotu šeit, aktivizējiet ierīci."</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Ierīce nav apstiprināta atskaņošanai."</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Šo multivides saturu nevar atskaņot šeit."</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Savienojums izveidots"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Savienojums izveidots, izmantojot ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Savienojums izveidots, izmantojot eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Noklusējuma iestatījums televizorā"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI izvade"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Iekšējie skaļruņi"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
<string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index fd4030427993..2ef4620d6e67 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Претпостави дека апликациите поддржуваат модерни формати"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Прикажувај известувања за транскодирање"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Оневозможи го кешот на транскодирањето"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Поставки за Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Резервен план за Force L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Изберете за да се овозможи резервен план за Force L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Активни услуги"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Погледнете и контролирајте услуги што се моментално активни"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Примена на WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Разбудете го уредот за да пуштате тука"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Уредот не е одобрен за репродукција"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Овие аудиовизуелни содржини не може да се пуштат тука"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Поврзано"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Поврзано преку ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Поврзано преку eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Стандардни поставки за телевизор"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Излез за HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Внатрешни звучници"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
<string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 066469c66c4c..2cb4da1330b0 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ആപ്പുകൾ ആധുനിക ഫോർമാറ്റുകളെ പിന്തുണയ്ക്കുമെന്ന് കരുതുക"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ട്രാൻസ്കോഡ് ചെയ്യൽ അറിയിപ്പുകൾ കാണിക്കുക"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ട്രാൻസ്കോഡ് ചെയ്യൽ കാഷെ പ്രവർത്തനരഹിതമാക്കുക"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"വൈഡ്‌ലൈൻ ക്രമീകരണം"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 ഫോൾബാക്ക് ഫോഴ്‌സ് ചെയ്യുക"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 ഫോൾബാക്ക് ഫോഴ്‌സ് ചെയ്യാൻ തിരഞ്ഞെടുക്കുക"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"പ്ലേ ചെയ്യാൻ ഉപകരണം സജീവമാക്കുക"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ഉപകരണത്തിന് പ്ലേ ചെയ്യാനുള്ള അനുമതിയില്ല"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ഈ മീഡിയ ഇവിടെ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"കണക്‌റ്റ് ചെയ്‌തു"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ടിവി ഡിഫോൾട്ട്"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ഔട്ട്പുട്ട്"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ആന്തരിക സ്‌പീക്കറുകൾ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്‌റ്റ് ചെയ്യുന്നതിൽ പ്രശ്‌നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
<string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്‌ബാക്കും"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index dba6d8d0201f..0a99650ce990 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Идэвхтэй, батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Идэвхтэй, Зүүн: Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Баруун: Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Зүүн: Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Баруун: Батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Зүүн тал <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Баруун тал <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Идэвхтэй"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Хадгалсан"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Идэвхтэй, зөвхөн зүүн тал"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Идэвхтэй, зөвхөн баруун тал"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Идэвхтэй, зүүн болон баруун тал"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Аппыг орчин үеийн форматыг дэмждэг гэж үздэг"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Хөрвүүлгийн мэдэгдэл харуулах"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Хөрвүүлгийн завсрын санах ойг идэвхгүй болгох"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-н тохиргоо"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 нөөцийг хүчлэх"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 нөөцийг хүчлэхийг сонгоно уу"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Ажиллаж байгаа үйлчилгээнүүд"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Одоо ажиллаж байгаа үйлчилгээнүүдийг харах болон хянах"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView хэрэгжилт"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Энд тоглуулахын тулд төхөөрөмжийг сэрээнэ үү"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Төхөөрөмжийг тоглуулахыг зөвшөөрөөгүй"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Энэ медиаг энд тоглуулах боломжгүй"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Холбогдсон"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-р холбогдсон"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-р холбогдсон"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ТВ-ийн өгөгдмөл"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI гаралт"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Дотоод чанга яригч"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string>
<string name="help_label" msgid="3528360748637781274">"Тусламж, санал хүсэлт"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index f6ddef2e9546..dca07e2f4577 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"अ‍ॅक्टिव्ह, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बॅटरी"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"अ‍ॅक्टिव्ह, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बॅटरी"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"बॅटरी <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"डावा <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"उजवा <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"अ‍ॅक्टिव्ह"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"सेव्ह केली आहेत"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"फक्त डावे अ‍ॅक्टिव्ह आहे"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"फक्त उजवे अ‍ॅक्टिव्ह आहे"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"डावे आणि उजवे अ‍ॅक्टिव्ह आहे"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"असे गृहीत धरा की, ॲप्स आधुनिक फॉरमॅटना सपोर्ट करतात"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ट्रान्सकोडिंग सूचना दाखवा"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ट्रान्सकोडिंग कॅशे बंद करा"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine सेटिंग्ज"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 फॉलबॅकची सक्ती करा"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 फॉलबॅकची सक्ती करण्यासाठी निवडा"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"येथे प्ले करण्यासाठी डिव्हाइस सुरू करा"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"डिव्हाइसला प्ले करण्यासाठी मंजुरी नाही"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"हा मीडिया येथे प्ले करू शकत नाही"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"कनेक्ट केलेला"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC द्वारे कनेक्ट केलेली"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC द्वारे कनेक्ट केलेली"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"टीव्ही डीफॉल्ट"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI आउटपुट"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"अंतर्गत स्पीकर"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्‍ट करण्‍यात समस्‍या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
<string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index d1aba52ed0aa..ad29abc1b5e0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktif, bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktif, Ki: bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ka: bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ki: bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ka: bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Kiri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktif"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Disimpan"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktif, kiri sahaja"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktif, kanan sahaja"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktif, kiri dan kanan"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Mengambil alih sokongan apl format moden"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Tunjukkan pemberitahuan transpengekodan"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Lumpuhkan cache transpengekodan"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Tetapan Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Paksa sandaran L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Pilih untuk memaksa sandaran L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Perkhidmatan dijalankan"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Lihat dan kawal perkhidmatan yang sedang dijalankan"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Pelaksanaan WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Bangkitkan peranti untuk main di sini"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Peranti tidak diluluskan untuk main"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Tidak dapat memainkan media ini di sini"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Disambungkan"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Disambungkan melalui ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Disambungkan melalui eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Tetapan Lalai TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Output HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Pembesar Suara Dalaman"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan &amp; hidupkan kembali peranti"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan &amp; maklum balas"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 8fa6a73cb5d8..d538dc82b7d1 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"ဖွင့်ထားသည်၊ ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"သုံးနေသည်၊ L− ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>၊ R− ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L− ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>၊ R− ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"ဘယ် <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"ညာ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ဖွင့်ထားသည်"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"သိမ်းထားသည်များ"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ဖွင့်ထားသည်၊ ဘယ်သီးသန့်"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ဖွင့်ထားသည်၊ ညာသီးသန့်"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ဖွင့်ထားသည်၊ ဘယ်နှင့် ညာ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ဤအက်ပ်များက ဖော်မက်အသစ်များကို ပံ့ပိုးသည်"</string>
<string name="transcode_notification" msgid="5560515979793436168">"အမျိုးအစားပြောင်းခြင်း အကြောင်းကြားချက်များကို ပြရန်"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"အမျိူးအစားပြောင်းခြင်း ကက်ရှ်ကို ပိတ်ရန်"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ဆက်တင်များ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 နောက်ပြန်ဆုတ်မှု မဖြစ်မနေလုပ်ခြင်း"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 နောက်ပြန်ဆုတ်မှု မဖြစ်မနေလုပ်ရန် ရွေးပါ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"အလုပ်လုပ်နေသောဝန်ဆောင်မှုများ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"လက်ရှိ ဝန်ဆောင်မှုများကို ကြည့်ရှု ထိန်းသိမ်းသည်"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView အကောင်အထည်ဖော်မှု"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ဒီမှာဖွင့်ရန် စက်ပစ္စည်းကိုနှိုးပါ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"စက်ပစ္စည်းက ဖွင့်ခွင့်မပြုပါ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ဤမီဒီယာကို ဒီမှာဖွင့်၍မရပါ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"ချိတ်ဆက်ထားသည်"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV မူရင်း"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI အထွက်"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"စက်ရှိ စပီကာများ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
<string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 84ba728b27c8..ae46b6860eaf 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiv, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batteri: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Høyre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Lagret"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, bare venstre"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, bare høyre"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, venstre og høyre"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Anta at apper støtter moderne formater"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Vis omkodingsvarsler"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Slå av omkodingsbuffer"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-innstillinger"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Tving bruk av L3-reservealternativet"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Velg for å tvinge bruk av L3-reservealternativet"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive tjenester"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Se og kontroller tjenester som kjører"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Vekk enheten for å spille her"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Enheten er ikke godkjent til å spille av"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Kan ikke spille av dette her"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Tilkoblet"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Tilkoblet via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Tilkoblet via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV-ens standardinnstilling"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-utdata"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interne høyttalere"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
<string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4e6d87f96f91..f73cd0b92107 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"सक्रिय, ब्याट्रीको स्तर: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"सक्रिय, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"ब्याट्रीको स्तर: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ब्याट्री <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"बायाँ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"दायाँ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"सक्रिय"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"सेभ गरिएको"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"बायाँ मात्र अन छ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"सक्रिय, दायाँ मात्र"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"सक्रिय, बायाँ र दायाँ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"एपहरूमा आधुनिक फर्म्याट प्रयोग गर्न मिल्छ भनी मान्नुहोस्"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ट्रान्सकोडिङसम्बन्धी सूचना देखाइयोस्"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ट्रान्सकोडिङको क्यास अफ गर्नुहोस्"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine को सेटिङ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 फलब्याक बलपूर्वक अन गर्नुहोस्"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 फलब्याकमा बलपूर्वक अन गर्ने कि नगर्ने भन्ने विकल्प चयन गर्नुहोस्"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"यो मिडिया यहाँ प्ले गर्न डिभाइस सक्रिय गर्नुहोस्"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"यो डिभाइसलाई मिडिया प्ले गर्ने अनुमति छैन"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"यो मिडिया यसमा प्ले गर्न मिल्दैन"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"कनेक्ट गरिएका"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC मार्फत कनेक्ट गरिएका"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC मार्फत कनेक्ट गरिएका"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"टिभीको डिफल्ट अडियो आउटपुट"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI आउटपुट"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"आन्तरिक स्पिकरहरू"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि अन गर्नुहोस्"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
<string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f20cb970eaa4..2a47d7efd7e0 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Aannemen dat apps moderne indelingen ondersteunen"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Transcoderingsmeldingen tonen"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Transcodering van cache uitzetten"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-instellingen"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3-fallback afdwingen"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecteren om L3-fallback af te dwingen"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Actieve services"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Bekijk en beheer services die momenteel actief zijn"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementatie"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activeer het apparaat om hier af te spelen"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Apparaat niet goedgekeurd voor afspelen"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Kan deze media hier niet afspelen"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Verbonden"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Verbonden via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Verbonden via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Tv-standaard"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-uitvoer"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interne speakers"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en feedback"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 534d217a0d73..65e4b2e97c28 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ଧରିନିଅନ୍ତୁ ଆପଗୁଡ଼ିକ ଆଧୁନିକ ଫର୍ମାଟଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ଟ୍ରାନ୍ସକୋଡିଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ଟ୍ରାନ୍ସକୋଡିଂ କ୍ୟାଶକୁ ଅକ୍ଷମ କରନ୍ତୁ"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ସେଟିଂସ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"ଫୋର୍ସ L3 ଫଲବେକ"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ଫୋର୍ସ L3 ଫଲବେକ ଚୟନ କରନ୍ତୁ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍‌ଭ୍ୟୁ ପ୍ରୟୋଗ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ଏଠାରେ ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ସକ୍ରିୟ କରନ୍ତୁ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ଅନୁମୋଦନ କରାଯାଇନାହିଁ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ଏଠାରେ ମିଡିଆ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ଟିଭିର ଡିଫଲ୍ଟ ଅଡିଓ ଆଉଟପୁଟ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ଆଉଟପୁଟ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ଇଣ୍ଟର୍ନଲ ସ୍ପିକର"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
<string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f0f656efbd28..4a72574540f0 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"ਕਿਰਿਆਸ਼ੀਲ, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"ਕਿਰਿਆਸ਼ੀਲ, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ਬੈਟਰੀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ਬੈਟਰੀ"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ਬੈਟਰੀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ਬੈਟਰੀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ਬੈਟਰੀ"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"ਖੱਬੇ ਪਾਸੇ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"ਸੱਜੇ ਪਾਸੇ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ਕਿਰਿਆਸ਼ੀਲ"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ਕਿਰਿਆਸ਼ੀਲ, ਸਿਰਫ਼ ਖੱਬਾ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ਕਿਰਿਆਸ਼ੀਲ, ਸਿਰਫ਼ ਸੱਜਾ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ਕਿਰਿਆਸ਼ੀਲ, ਖੱਬਾ ਅਤੇ ਸੱਜਾ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ਮੰਨ ਲਓ ਕਿ ਐਪਾਂ ਆਧੁਨਿਕ ਫਾਰਮੈਟਾਂ ਦਾ ਸਮਰਥਨ ਕਰਦੀਆਂ ਹਨ"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਕੈਸ਼ੇ ਬੰਦ ਕਰੋ"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ਸੈਟਿੰਗਾਂ"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"ਜ਼ਬਰਦਸਤੀ L3 ਫਾਲਬੈਕ ਕਰੋ"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ਜ਼ਬਰਦਸਤੀ L3 ਫਾਲਬੈਕ ਕਰਨ ਲਈ ਚੁਣੋ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲੀਕਰਨ"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ਇੱਥੇ ਮੀਡੀਆ ਚਲਾਉਣ ਲਈ ਡੀਵਾਈਸ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ਡੀਵਾਈਸ ਨੂੰ ਮੀਡੀਆ ਚਲਾਉਣ ਦੀ ਮਨਜ਼ੂਰੀ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ਇਸ ਮੀਡੀਆ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ਟੀਵੀ ਦਾ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਆਊਟਪੁੱਟ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ਆਊਟਪੁੱਟ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ਅੰਦਰੂਨੀ ਸਪੀਕਰ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
<string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index af9b73f7e81f..61ae709fbf01 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktywne, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterii"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktywna, L: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Po lewej <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Po prawej <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Urządzenie aktywne"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Zapisano"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywne, tylko lewa strona"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywne, tylko prawa strona"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktywny, lewa i prawa strona"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Zakładaj, że aplikacje obsługują nowoczesne formaty"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Pokaż powiadomienia transkodowania"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Wyłącz pamięć podręczną transkodowania"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Ustawienia Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Wymuszaj kreację zastępczą L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Wybierz wymuszanie kreacji zastępczej L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Uruchomione usługi"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Wyświetl obecnie uruchomione usługi i nimi zarządzaj"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacja WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wybudź, aby tu odtworzyć"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Urządzenie nie ma uprawnień do odtwarzania"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Nie można odtworzyć tego pliku multimedialnego"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Połączono"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Połączono przez ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Połączono przez eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Wyjście domyślne telewizora"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Wyjście HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Głośniki wewnętrzne"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 78e8679fd268..20c0460d723d 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Considerar que os apps são compatíveis com formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Desativar cache da transcodificação"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Configurações o Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forçar substituição do L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecione para forçar a substituição do L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços em execução no momento"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ativar dispositivo para tocar aqui"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"O dispositivo não foi aprovado para reprodução"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Não é possível reproduzir essa mídia aqui"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Padrão da TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Saída HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Alto-falantes internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 1be30d187f85..8c8482f9a0ad 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Assumir que as apps suportam formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Desativar cache de transcodificação"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Definições do Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forçar utilização alternativa do L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecione para forçar a utilização alternativa do L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços actualmente em execução"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ative o dispositivo para reproduzir aqui"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Dispositivo não aprovado para reprodução"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Não é possível reproduzir este conteúdo multimédia aqui"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Ligado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ligado através de ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ligado através de eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Predefinição da TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Saída HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Altifalantes internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e comentários"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 78e8679fd268..20c0460d723d 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Considerar que os apps são compatíveis com formatos modernos"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Desativar cache da transcodificação"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Configurações o Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forçar substituição do L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selecione para forçar a substituição do L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços em execução no momento"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ativar dispositivo para tocar aqui"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"O dispositivo não foi aprovado para reprodução"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Não é possível reproduzir essa mídia aqui"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectado"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Padrão da TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Saída HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Alto-falantes internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4c6fef648cb7..4b4c0752b7cd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Activ, baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Activ, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterie, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterie"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterie, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterie"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activ"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Salvat"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activ, numai stânga"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activ, numai dreapta"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activ, stânga și dreapta"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Presupune că aplicațiile acceptă formatele moderne"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Vezi notificările privind transcodarea"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Dezactivează memoria cache pentru transcodare"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Setări Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Forțează alternativa L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Selectează pentru a forța alternativa L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Servicii în curs de funcționare"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Vezi și controlează serviciile care funcționează în prezent"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementare WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activează dispozitivul pentru a reda aici"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Dispozitivul nu este aprobat pentru redare"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Conținutul media nu poate fi redat aici"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Conectat"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectat prin ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectat prin eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Prestabilită pentru televizor"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Ieșire HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Difuzoare interne"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Oprește și repornește dispozitivul."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
<string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ec648021451d..53f27299f2f4 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Активно. Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активно. Л: батарея <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; П: батарея <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Уровень заряда: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батарея <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Л: батарея <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; П: батарея <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Левый <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Правый <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активно"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сохранено"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активен, только левое ухо"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активен, только правое ухо"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активен, оба уха"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Считать, что приложения поддерживают современные форматы кодирования"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Показывать уведомления о перекодировании"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Отключить кеш перекодирования"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Настройки Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Принудительный переход к L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Выберите, чтобы использовать принудительный переход к L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Работающие службы"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Просмотр работающих служб и управление ими"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Сервис WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Для воспроизведения выведите устройство из спящего режима."</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Для воспроизведения необходима авторизация."</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Невозможно воспроизвести медиафайл."</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Подключено"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Подключено через ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Подключено через eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Стандартный выход на телевизоре"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Выход HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Внутренние динамики"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
<string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 2aa69e543d69..d7dd1d09fddd 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"ක්‍රියාකාරී, බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"ක්‍රියාත්මක, ව: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ද: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"ව: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ද: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"වම <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"දකුණ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ක්‍රියාකාරී"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"සුරැකිණි"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"සක්‍රිය, වම පමණි"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"සක්‍රිය, දකුණ පමණි"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"සක්‍රිය, වම සහ දකුණ"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"යෙදුම් නවීන ආකෘති සඳහා සහාය දක්වයි යැයි උපකල්පනය කරමු"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ට්‍රාන්ස්කෝඩින් දැනුම්දීම් පෙන්වන්න"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ට්‍රාන්ස්කොඩින් හැඹිලිය අබල කරන්න"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine සැකසීම්"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 පසුබැසීමට බල කරන්න"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 පසුබැසීමට බල කිරීමට තෝරන්න"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"ධාවනය වන සේවා"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"දැනට ධාවනය වන සේවා බලන්න සහ පාලනය කරන්න"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ක්‍රියාත්මක කිරීම"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"මෙහි වාදනය කිරීමට උපාංගය අවදි කරන්න"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"උපාංගය වාදනය කිරීමට අනුමත කර නැත"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"මෙම මාධ්‍ය මෙහි වාදනය කළ නොහැක"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"සම්බන්ධිතයි"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC හරහා සම්බන්ධ කර ඇත"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC හරහා සම්බන්ධ කර ඇත"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"රූපවාහිනී පෙරනිමිය"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI ප්‍රතිදානය"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"අභ්‍යන්තර ස්පීකර්"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්‍රියාවිරහිත කර &amp; ආපසු ක්‍රියාත්මක කරන්න"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string>
<string name="help_label" msgid="3528360748637781274">"උදවු &amp; ප්‍රතිපෝෂණ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 1f5ebffd0443..4b83ba0678da 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktívne, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktívne, Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batérie, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batérie, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Ľavé: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Pravé: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktívne"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Uložené"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktívne, iba ľavá strana"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktívne, iba pravá strana"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktívne, ľavá aj pravá strana"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Prepdokladať, že aplikácie podporujú moderné formáty"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Zobraziť upozornenia prekódovania"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Deaktivácia vyrovnávacej pamäte prekódovania"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Nastavenia softvéru Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Vynútiť zálohu L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Vyberte, keď chcete vynútiť zálohu L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Spustené služby"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Zobrazovať a riadiť aktuálne spustené služby"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Implementácia WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ak chcete prehrávať obsah tu, prebuďte zariadenie"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Prehrávanie v tomto zariadení nie je schválené"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Médium sa tu nedá prehrať"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Pripojené"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Pripojené prostredníctvom rozhrania ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Pripojené prostredníctvom rozhrania eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Predvolené nastavenie televízora"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Výstup HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Vnútorné reproduktory"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
<string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 4809cbb4dce5..c70716d294f2 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktivno, baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivno, L: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktivna"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Shranjeno"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo levo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, samo desno"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, levo in desno"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Aplikacije naj bi podpirale sodobne oblike zapisov"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Prikaz obvestil o prekodiranju"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Onemogoči predpomnilnik za prekodiranje"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Nastavitve za Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Vsili nadomestno varnostno raven L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Izbira vsiljenja nadomestne varnostne ravni L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Zagnane storitve"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Preglejte in nadzorujte storitve, ki so trenutno zagnane."</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Izvedba spletnega pogleda"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Zbudite napravo za predvajanje tu"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Naprava ni odobrena za predvajanje."</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Te predstavnosti ni mogoče predvajati tukaj."</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Povezano"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano prek zvočnega kanala ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano prek zvočnega kanala eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Privzeti izhod televizorja"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Izhod HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Notranji zvočniki"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 627ad8f960f3..a02ca5a6f549 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiv, L: Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: Bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> majtas"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> djathtas"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Të ruajtura"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktive, vetëm majtas"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktive, vetëm djathtas"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktive, majtas dhe djathtas"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Supozo se aplikacionet i mbështetin formatet moderne"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Shfaq njoftimet e transkodimit"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Çaktivizo memorien specifike të transkodimit"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Cilësimet e Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Detyro kalimin përsëri te L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Zgjidh që të detyrosh kalimin përsëri te L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Shërbimet në ekzekutim"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Shiko dhe kontrollo shërbimet që po ekzekutohen aktualisht"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Zbatimi i WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Riaktivizoje pajisjen për të luajtur këtu"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Pajisja nuk është miratuar për të luajtur"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Kjo media nuk mund të luhet këtu"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Lidhur"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Lidhur përmes ARC-së"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Lidhur përmes eARC-së"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Parazgjedhja e televizorit"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Dalja HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Altoparlantët e brendshëm"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4bac27095601..bc0114f9756c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Подразумевај да апликације подржавају модерне формате"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Приказуј обавештења о транскодирању"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Онемогући кеш транскодирања"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine подешавања"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Принудно примени L3 резерву"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Изаберите да бисте принудно применили L3 резерву"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Покренуте услуге"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Приказ и контрола тренутно покренутих услуга"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Примена WebView-а"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Пробудите уређај да бисте пустили овде"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Уређај није одобрен за репродукцију"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Не можете да пустите овај медијски фајл овде"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Повезано"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Повезано преко ARC-а"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Повезано преко eARC-а"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Подразумевана вредност за ТВ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI излаз"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Унутрашњи звучници"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string>
<string name="help_label" msgid="3528360748637781274">"Помоћ и повратне информације"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b20263550dae..51e00b701ea5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktiv, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri. H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batteri: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri. H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Vänster <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Höger <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Sparad"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, bara vänster"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, bara höger"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, vänster och höger"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Anta att appar har stöd för moderna format"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Visa aviseringar för omkodning"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Inaktivera cacheminne för omkodning"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine-inställningar"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Tvinga L3-alternativ"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Välj för att tvinga L3-alternativ"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Aktiva tjänster"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Visa och styr aktiva tjänster"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Väck enheten för att spela upp här"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Enheten är inte godkänd"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Det går inte att spela upp detta här"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Ansluten"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ansluten via ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ansluten via eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Standardinställning för tv:n"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI-utgång"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Interna högtalare"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c63b61044afe..6bacc324c9c0 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Chukulia kuwa programu zinatumia miundo ya kisasa"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Onyesha arifa za kubadilisha muundo wa faili"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Zima kipengele cha akiba ya kubadilisha muundo wa faili"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Mipangilio ya Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Lazimisha chaguo mbadala ya L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Teua ili ulazimishe chaguo mbadala ya L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Huduma zinazoendeshwa"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Onyesha na udhibiti huduma zinazoendeshwa kwa sasa"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Utekelezaji wa WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Washa skrini ya kifaa ili ucheze maudhui hapa"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Kifaa hakijaidhinishwa ili kucheza maudhui"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Huwezi kucheza maudhui haya hapa"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Imeunganishwa"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Imeunganishwa kupitia ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Imeunganishwa kupitia eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Mipangilio Chaguomsingi ya Televisheni"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Kutoa Sauti Kupitia HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Spika za Ndani"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string>
<string name="help_label" msgid="3528360748637781274">"Usaidizi na maoni"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 49351e8c3da9..07bdaf2ff9e5 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"செயலில் உள்ளது, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"செயலில் உள்ளது, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> பேட்டரி, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> பேட்டரி"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"பேட்டரி <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> பேட்டரி, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> பேட்டரி"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"இடதுபுறம்: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"வலதுபுறம்: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"செயலில் உள்ளது"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"சேமிக்கப்பட்டது"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"இடது பக்கம் மட்டும் செயலில் உள்ளது"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"வலது பக்கம் மட்டும் செயலில் உள்ளது"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"வலது மற்றும் இடது பக்கம் செயலில் உள்ளது"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ஆப்ஸ் மாடர்ன் வடிவங்களை ஆதரிக்கும்படி அமை"</string>
<string name="transcode_notification" msgid="5560515979793436168">"குறிமாற்ற அறிவிப்புகளைக் காட்டு"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"குறிமாற்றத்திற்கான தற்காலிக சேமிப்பை முடக்குதல்"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine அமைப்புகள்"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 ஃபால்பேக்கை இயக்குதல்"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 ஃபால்பேக்கை இயக்குவதற்குத் தேர்ந்தெடுக்கலாம்"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"இயங்கும் சேவைகள்"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"தற்போது இயக்கத்தில் இருக்கும் சேவைகளைப் பார்த்து கட்டுப்படுத்து"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"இங்கே பிளே செய்ய உங்கள் சாதனத்தைச் சார்ஜ் செய்யுங்கள்"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"பிளே செய்வதற்குச் சாதனம் அனுமதிக்கப்படவில்லை"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"இந்த மீடியாவை இங்கே பிளே செய்ய முடியவில்லை"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"இணைக்கப்பட்டுள்ளது"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC மூலம் இணைக்கப்பட்டுள்ளவை"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC மூலம் இணைக்கப்பட்டுள்ளவை"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"டிவி இயல்புநிலை"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI அவுட்புட்"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"உட்புற ஸ்பீக்கர்கள்"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
<string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 89e7bb7e4065..81739b396a3e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"యాప్‌లు ఆధునిక ఫార్మాట్‌లకు సపోర్ట్ చేస్తాయని అనుకోండి"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ట్రాన్స్‌కోడింగ్ నోటిఫికేషన్‌లను చూపండి"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ట్రాన్స్‌కోడింగ్ కాష్‌ను డిజేబుల్ చేయండి"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine సెట్టింగ్‌లు"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"ఫోర్స్ L3 ఫాల్‌బ్యాక్"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"ఫోర్స్ L3 ఫాల్‌బ్యాక్‌ను ఎంచుకోండి"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సర్వీస్‌లు"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సర్వీస్‌లను చూడండి, కంట్రోల్‌ చేయండి"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ఇక్కడ ప్లే చేయడానికి పరికరాన్ని మేల్కొలపండి"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ప్లే చేయడానికి పరికరానికి అనుమతి లేదు"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ఈ మీడియాను ఇక్కడ ప్లే చేయడం సాధ్యపడదు."</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"కనెక్ట్ అయింది"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ద్వారా కనెక్ట్ చేయబడింది"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ద్వారా కనెక్ట్ చేయబడింది"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"టీవీ ఆటోమేటిక్ సెట్టింగ్"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI అవుట్‌పుట్"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"అంతర్గత స్పీకర్‌లు"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
<string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index aecfc6fbba94..001af0163801 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"ถือว่าแอปรองรับรูปแบบสมัยใหม่"</string>
<string name="transcode_notification" msgid="5560515979793436168">"แสดงการแจ้งเตือนการแปลง"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ปิดใช้แคชสำหรับการแปลง"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"การตั้งค่า Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 สำรอง"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"เลือกเพื่อเปิดใช้ Force L3 สำรอง"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"บริการที่ทำงานอยู่"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"ดูและควบคุมบริการที่ทำงานอยู่"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"การใช้งาน WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ปลุกระบบอุปกรณ์เพื่อเล่นที่นี่"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"อุปกรณ์ไม่อนุมัติให้เล่น"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"เล่นสื่อนี้ที่นี่ไม่ได้"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"เชื่อมต่อแล้ว"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"เชื่อมต่อผ่าน ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"เชื่อมต่อผ่าน eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"ค่าเริ่มต้นของทีวี"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"เอาต์พุต HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"ลำโพงของเครื่อง"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string>
<string name="help_label" msgid="3528360748637781274">"ความช่วยเหลือและความคิดเห็น"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index c579dea4580b..9ccc801e5b4b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Ipagpalagay na sinusuportahan ng mga app ang mga modernong format"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Ipakita ang mga notification sa pag-transcode"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"I-disable ang cache ng pag-transcode"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Mga setting ng Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Puwersahin ang L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Piliin para puwersahin ang L3 fallback"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Mga tumatakbong serbisyo"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Tingnan at kontrolin ang mga kasalukuyang tumatakbong serbisyo"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Pagpapatupad sa WebView"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"I-wake ang device para i-play dito"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Hindi naaprubahan ang device para sa pag-play"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Hindi ma-play ang media na ito rito"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Nakakonekta"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Nakakonekta sa pamamagitan ng ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Nakakonekta sa pamamagitan ng eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Naka-default sa TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Output"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Mga Internal na Speaker"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Tulong at feedback"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 64cb61b9cbdd..a49f3ab13e99 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Etkin, pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Etkin, Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pil, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Pil <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pil, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Etkin"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Kaydedildi"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Yalnızca sol tarafta etkin"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Yalnızca sağ tarafta etkin"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Sol ve sağ tarafta etkin"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Uygulamaların modern biçimleri desteklediğini varsay"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Kod dönüştürme bildirimlerini göster"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Kod dönüştürme önbelleğini devre dışı bırak"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine ayarları"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"L3 yedeğiniz zorunlu tut"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"L3 yedeğini zorunlu tutmak için seçin"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Çalışan hizmetler"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Şu anda çalışan hizmetleri görüntüle ve denetle"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Web Görünümü kullanımı"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Burada oynatmak için cihazı uyandırın"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Cihaz, oynatma işlemini onaylamadı"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Bu medya burada oynatılamıyor"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Bağlandı"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ile bağlandı"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ile bağlandı"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"TV Varsayılanı"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI Çıkışı"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Dahili Hoparlörler"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index a52a5e72b2c3..eba792681068 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Активовано, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активний. Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> заряду акумулятора, П: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Заряд акумулятора: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> заряду акумулятора, П: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Ліва частина: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Права частина: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активовано"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Збережено"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активовано, лише лівий"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активовано, лише правий"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активовано, лівий і правий"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Вважати, що додатки підтримують сучасні формати"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Показувати сповіщення про перекодування"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Вимкнути кеш перекодування"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Налаштування Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Примусово повернутися на рівень безпеки 3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Виберіть, щоб примусово повернутися на рівень безпеки 3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Запущені сервіси"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Переглянути й налаштувати запущені сервіси"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Застосування WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Виведіть із режиму сну, щоб відтворювати"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Не схвалено для відтворення"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Цей медіаконтент не підтримується"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Підключено"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Підключено через ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Підключено через eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Стандартні налаштування телевізора"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Вихід HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Внутрішні динаміки"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
<string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 0d4dc6c462aa..ce5ed31abaef 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"فرض کریں کہ ایپس جدید فارمیٹس کو سپورٹ کرتی ہیں"</string>
<string name="transcode_notification" msgid="5560515979793436168">"ٹرانسکوڈنگ اطلاعات دکھائیں"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ٹرانسکوڈنگ کیش غیر فعال کریں"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"‏Widevine کی ترتیبات"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"‏Force L3 fallback منتخب کریں"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"‏WebView کا نفاذ"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"یہاں چلانے کے لیے آلہ کو اٹھائیں"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"آلہ کو چلانے کے لیے اجازت نہیں دی گئی ہے"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"یہاں اس میڈیا کو چلایا نہیں جا سکتا"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"منسلک ہے"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"‏ARC کے ذریعے منسلک ہے"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"‏eARC کے ذریعے منسلک ہے"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"‏TV ڈیفالٹ"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"‏HDMI آؤٹ پٹ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"اندرونی اسپیکرز"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
<string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 2be1fcda2960..5d23a9355a08 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Faol, batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Faol, L: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batareya: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Chapda: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Oʻngda: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Faol"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saqlangan"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Faol, faqat chap"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Faol, faqat oʻng"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Faol, chap va oʻng"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Ilovalarda zamonaviy kodlash formatlari ishlaydi deb hisoblash"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Transkripsiya bildirishnomalarini chiqarish"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Transkripsiya keshini faolsizlantirish"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine sozlamalari"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Majburiy L3 zaxirasi"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Majburiy L3 zaxirasini tanlash"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Ishlab turgan ilovalar"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Ishlab turgan ilovalarni ko‘rish va boshqarish"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ta’minotchisi"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ijro etish uchun qurilmani uyqu rejimidan chiqaring."</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Qurilmada ijro ruxsati yoʻq"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Media fayl ijro etilmaydi"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Ulangan"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC orqali ulangan"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC orqali ulangan"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Televizorda birlamchi"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI chiqishi"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Ichki karnaylar"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string>
<string name="help_label" msgid="3528360748637781274">"Yordam/fikr-mulohaza"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index bc903d7b4eba..fb870c4e679c 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Đang hoạt động, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Đang hoạt động, Trái: Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Phải: Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Trái: Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Phải: Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Bên trái <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Bên phải <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Đang hoạt động"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Đã lưu"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Đang hoạt động, chỉ tai bên trái"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Đang hoạt động, chỉ tai phải"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Đang hoạt động, cả tai phải và tai trái"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Giả định rằng các ứng dụng hỗ trợ định dạng hiện đại"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Hiện thông báo chuyển mã"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Vô hiệu hóa bộ nhớ đệm dùng để chuyển mã"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Cài đặt Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Buộc chuyển sang phương án dự phòng L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Chọn để buộc chuyển sang phương án dự phòng L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Các dịch vụ đang chạy"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Xem và kiểm soát các dịch vụ đang chạy"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Triển khai WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Đánh thức thiết bị để phát tại đây"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Thiết bị chưa được phép phát"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Không thể phát nội dung nghe nhìn này tại đây"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Đã kết nối"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Đã kết nối qua ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Đã kết nối qua eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"Chế độ mặc định của TV"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Đầu ra HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Loa trong"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string>
<string name="help_label" msgid="3528360748637781274">"Trợ giúp và phản hồi"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 007c797ba08a..2174e12a78df 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -436,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"假设应用支持现代格式"</string>
<string name="transcode_notification" msgid="5560515979793436168">"显示转码通知"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"停用转码缓存"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine 设置"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"强制执行 L3 回退"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"选择即可强制执行 L3 回退"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"正在运行的服务"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"查看和控制当前正在运行的服务"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 实现"</string>
@@ -565,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"若要在此设备上播放,请唤醒设备"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"设备未获准播放"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"无法在此设备上播放该媒体内容"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"已连接"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已通过 ARC 连接"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已通过 eARC 连接"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"电视默认设置"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI 输出"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"内置扬声器"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
<string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index f454a68bfa5c..dc490eea4d49 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"使用中,電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"已啟用,左:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 電量,右:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"左:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 電量,右:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"左耳機:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"右耳機:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"使用中"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"已儲存"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"使用中,僅左耳"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"使用中,僅右耳"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"使用中,左右耳"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"假設應用程式支援新型格式"</string>
<string name="transcode_notification" msgid="5560515979793436168">"顯示轉碼通知"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"停用轉碼快取"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine 設定"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"強制退回 L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"選取即可強制退回 L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"必須喚醒裝置才能在此播放"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"裝置未獲核准播放"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"無法在此播放此媒體內容"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"已連接"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已透過 ARC 連接"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已透過 eARC 連接"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"電視預設的音訊輸出裝置"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI 輸出"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"內置喇叭"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 1d0403099824..504901106be5 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"使用中,電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"已啟用,左:目前電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右:目前電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"左:目前電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右:目前電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"左耳機:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"右耳機:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"使用中"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"已儲存"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"使用中,僅左耳"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"使用中,僅右耳"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"使用中,左右耳"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"假設應用程式支援新格式"</string>
<string name="transcode_notification" msgid="5560515979793436168">"顯示轉碼通知"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"停用轉碼快取"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Widevine 設定"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"強制退回 L3"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"選取即可強制退回 L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"正在運作的服務"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並管理目前正在執行的服務"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 實作"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"必須喚醒裝置才能在此播放"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"裝置未獲准播放"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"無法在此播放這個媒體內容"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"已連線"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"透過 ARC 連線"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"透過 eARC 連線"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"電視預設的音訊輸出裝置"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"HDMI 輸出裝置"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"內建喇叭"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見回饋"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index c0c3b4834755..7c82c19b1bfc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -97,16 +97,12 @@
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Kuyasebenza, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Kuyasebenza, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ibhethri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ibhethri"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri"</string>
- <!-- no translation found for tv_bluetooth_battery_level (8786353985605532846) -->
- <skip />
+ <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Ibhethri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ibhethri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ibhethri"</string>
- <!-- no translation found for bluetooth_battery_level_untethered_left (2952823007648782646) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right (6525710737740083276) -->
- <skip />
+ <string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Kwesokunxele <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right" msgid="6525710737740083276">"Kwesokudla <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Iyasebenza"</string>
- <!-- no translation found for bluetooth_saved_device (4895871321722311428) -->
- <skip />
+ <string name="bluetooth_saved_device" msgid="4895871321722311428">"Ilondoloziwe"</string>
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Iyasebenza, ngakwesokunxele kuphela"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Iyasebenza, ngakwesokudla kuphela"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Iyasebenza, ngakwesokunxele nakwesokudla"</string>
@@ -440,12 +436,9 @@
<string name="transcode_default" msgid="3784803084573509491">"Kuthathe njengokungathi izinhlelo zokusebenza zisekela amafomethi esimanje"</string>
<string name="transcode_notification" msgid="5560515979793436168">"Bonisa izaziso zokudlulisela ikhodi"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Khubaza inqolobane yokudlulisela ikhodi"</string>
- <!-- no translation found for widevine_settings_title (4023329801172572917) -->
- <skip />
- <!-- no translation found for force_l3_fallback_title (4987972688770202547) -->
- <skip />
- <!-- no translation found for force_l3_fallback_summary (3080790841069996016) -->
- <skip />
+ <string name="widevine_settings_title" msgid="4023329801172572917">"Amasethingi e-Widevine"</string>
+ <string name="force_l3_fallback_title" msgid="4987972688770202547">"Force L3 fallback"</string>
+ <string name="force_l3_fallback_summary" msgid="3080790841069996016">"Khetha ukuphoqa isihibe sika-L3"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Amasevisi asebenzayo"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Buka futhi ulawule amasevisi asebenzayo okwamanje"</string>
<string name="select_webview_provider_title" msgid="3917815648099445503">"Ukufakwa ke-WebView"</string>
@@ -569,22 +562,14 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Vusa idivayisi ukuze udlale lapha"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"Idivayisi ayigunyaziwe ukuthi idlale"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Awukwazi ukudlala le midiya lapha"</string>
- <!-- no translation found for tv_media_transfer_connected (5145011475885290725) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_fallback_title (3674360098755328601) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_fallback_title (3098685494578519940) -->
- <skip />
- <!-- no translation found for tv_media_transfer_arc_subtitle (1040017851325069082) -->
- <skip />
- <!-- no translation found for tv_media_transfer_earc_subtitle (645191413103303077) -->
- <skip />
- <!-- no translation found for tv_media_transfer_default (38102257053315304) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi (2229000864416329818) -->
- <skip />
- <!-- no translation found for tv_media_transfer_internal_speakers (2433193551482972117) -->
- <skip />
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"Ixhunyiwe"</string>
+ <string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"I-HDMI ARC"</string>
+ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"I-HDMI eARC"</string>
+ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ixhunywe nge-ARC"</string>
+ <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ixhunywe nge-eARC"</string>
+ <string name="tv_media_transfer_default" msgid="38102257053315304">"I-TV Ezenzakalelayo"</string>
+ <string name="tv_media_transfer_hdmi" msgid="2229000864416329818">"Okukhishwayo kwe-HDMI"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="2433193551482972117">"Izipikha Zangaphakathi"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
<string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 03d1cda9c071..e77964c6fa0c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -243,6 +243,32 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
}
/**
+ * Checks whether add user is disabled on the device
+ *
+ * @param context {@link Context} for the calling user.
+ *
+ *
+ * @param userId User to check enforced admin status for.
+ *
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} If adding user is not disabled.
+ */
+ public static EnforcedAdmin checkIfAddUserDisallowed(Context context, int userId) {
+ final UserManager um = UserManager.get(context);
+ if (!um.hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.of(userId))) {
+ // Restriction is not enforced.
+ return null;
+ }
+ EnforcedAdmin enforcedAdmin = checkIfRestrictionEnforced(context,
+ UserManager.DISALLOW_ADD_USER, userId);
+ if (enforcedAdmin != null) {
+ return enforcedAdmin;
+ }
+ return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(
+ UserManager.DISALLOW_ADD_USER);
+ }
+
+ /**
* Check if an application is suspended.
*
* @return EnforcedAdmin Object containing the enforced admin component and admin user details,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f5bacb62b6b2..c97445f04eea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -230,30 +230,30 @@ public class BluetoothEventManager {
@VisibleForTesting
void dispatchActiveDeviceChanged(
- @Nullable CachedBluetoothDevice activeDevice,
- int bluetoothProfile) {
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ CachedBluetoothDevice targetDevice = activeDevice;
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
- Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
- boolean isActive = Objects.equals(cachedDevice, activeDevice);
- if (!isActive && !memberSet.isEmpty()) {
- for (CachedBluetoothDevice memberCachedDevice : memberSet) {
- isActive = Objects.equals(memberCachedDevice, activeDevice);
- if (isActive) {
- Log.d(TAG,
- "The active device is the member device "
- + activeDevice.getDevice().getAnonymizedAddress()
- + ". change activeDevice as main device "
- + cachedDevice.getDevice().getAnonymizedAddress());
- activeDevice = cachedDevice;
- break;
- }
- }
+ // should report isActive from main device or it will cause trouble to other callers.
+ CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ CachedBluetoothDevice finalTargetDevice = targetDevice;
+ if (targetDevice != null
+ && ((subDevice != null && subDevice.equals(targetDevice))
+ || cachedDevice.getMemberDevice().stream().anyMatch(
+ memberDevice -> memberDevice.equals(finalTargetDevice)))) {
+ Log.d(TAG,
+ "The active device is the sub/member device "
+ + targetDevice.getDevice().getAnonymizedAddress()
+ + ". change targetDevice as main device "
+ + cachedDevice.getDevice().getAnonymizedAddress());
+ targetDevice = cachedDevice;
}
- cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
+ boolean isActiveDevice = cachedDevice.equals(targetDevice);
+ cachedDevice.onActiveDeviceChanged(isActiveDevice, bluetoothProfile);
mDeviceManager.onActiveDeviceChanged(cachedDevice);
}
+
for (BluetoothCallback callback : mCallbacks) {
- callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+ callback.onActiveDeviceChanged(targetDevice, bluetoothProfile);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index fa8c1fba6780..0ffcc45b6466 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -594,6 +594,16 @@ public class BluetoothUtils {
|| cachedDevice.isActiveDevice(BluetoothProfile.LE_AUDIO);
}
+ /**
+ * Check if the Bluetooth device is an active LE Audio device
+ *
+ * @param cachedDevice the CachedBluetoothDevice
+ * @return if the Bluetooth device is an active LE Audio device
+ */
+ public static boolean isActiveLeAudioDevice(CachedBluetoothDevice cachedDevice) {
+ return cachedDevice.isActiveDevice(BluetoothProfile.LE_AUDIO);
+ }
+
private static boolean isDeviceConnected(CachedBluetoothDevice cachedDevice) {
if (cachedDevice == null) {
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e9f4b5c8efc6..245fe6edd601 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1310,7 +1310,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// Set default string with battery level in device connected situation.
if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
stringRes = R.string.bluetooth_battery_level_untethered;
- } else if (batteryLevelPercentageString != null) {
+ } else if (batteryLevelPercentageString != null && !shortSummary) {
stringRes = R.string.bluetooth_battery_level;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 8c316d1c4f21..13635c3a8256 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -412,6 +412,21 @@ public class BluetoothEventManagerTest {
}
@Test
+ public void dispatchActiveDeviceChanged_activeFromSubDevice_mainCachedDeviceActive() {
+ CachedBluetoothDevice subDevice = new CachedBluetoothDevice(mContext, mLocalProfileManager,
+ mDevice3);
+ mCachedDevice1.setSubDevice(subDevice);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ Collections.singletonList(mCachedDevice1));
+ mCachedDevice1.onProfileStateChanged(mHearingAidProfile,
+ BluetoothProfile.STATE_CONNECTED);
+
+ assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+ mBluetoothEventManager.dispatchActiveDeviceChanged(subDevice, BluetoothProfile.HEARING_AID);
+ assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+ }
+
+ @Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
index 21cdc492e4ea..b3d66aa8bc3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
@@ -259,8 +259,10 @@ public class ActivityTileTest {
}
@Test
- public void isSearchable_noMetadata_isTrue() {
- final Tile tile = new ActivityTile(null, "category");
+ public void isSearchable_nullMetadata_isTrue() {
+ mActivityInfo.metaData = null;
+
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isSearchable()).isTrue();
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 7e8fe7e09d74..d9fe7335dbcb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -445,5 +445,6 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_SUPPORTED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.CONNECTIVITY_KEEP_DATA_ON, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 27a45dfc552e..3e0d05cd9ecf 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -391,6 +391,7 @@ public class SettingsHelper {
case Settings.Secure.TOUCH_EXPLORATION_ENABLED:
case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+ case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED:
case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED:
return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
case Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES:
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2e174e267bde..f1b53edbb1cd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -233,7 +233,6 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
- Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR,
Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DISABLE_WINDOW_BLURS,
@@ -686,7 +685,8 @@ public class SettingsBackupTest {
Settings.Global.Wearable.PHONE_SWITCHING_SUPPORTED,
Settings.Global.Wearable.WEAR_MEDIA_CONTROLS_PACKAGE,
Settings.Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE,
- Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED);
+ Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED,
+ Settings.Global.Wearable.CONNECTIVITY_KEEP_DATA_ON);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ed03d94b44c0..650319fd58f9 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -19,6 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.shell"
coreApp="true"
+ updatableSystem="false"
android:sharedUserId="android.uid.shell"
>
@@ -325,6 +326,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
+ <uses-permission android:name="android.permission.QUARANTINE_APPS" />
<uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<!-- Permission needed to wipe the device for Test Harness Mode -->
@@ -867,6 +869,8 @@
<!-- Permissions required for CTS test - CtsVoiceInteractionTestCases -->
<uses-permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT" />
<uses-permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA" />
+ <uses-permission android:name="android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO" />
+
<uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" />
<application
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 0e9f8b153fd4..9ab9ba8bbfed 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -224,6 +224,11 @@ android_library {
extra_check_modules: ["SystemUILintChecker"],
warning_checks: ["MissingApacheLicenseDetector"],
},
+ errorprone: {
+ javacflags: [
+ "-Xep:InvalidPatternSyntax:WARN",
+ ],
+ },
}
filegroup {
@@ -246,11 +251,9 @@ filegroup {
srcs: [
/* Status bar fakes */
"tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt",
"tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt",
/* QS fakes */
"tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
@@ -263,6 +266,7 @@ filegroup {
srcs: [
/* Keyguard converted tests */
// data
+ "tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt",
"tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt",
"tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt",
"tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt",
@@ -285,6 +289,7 @@ filegroup {
"tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt",
"tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt",
"tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
+ "tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
@@ -548,6 +553,11 @@ android_library {
test: true,
extra_check_modules: ["SystemUILintChecker"],
},
+ errorprone: {
+ javacflags: [
+ "-Xep:InvalidPatternSyntax:WARN",
+ ],
+ },
}
android_app {
@@ -589,6 +599,12 @@ android_app {
},
plugins: ["dagger2-compiler"],
+
+ errorprone: {
+ javacflags: [
+ "-Xep:InvalidPatternSyntax:WARN",
+ ],
+ },
}
android_robolectric_test {
@@ -602,6 +618,7 @@ android_robolectric_test {
":SystemUI-tests-multivalent",
],
static_libs: [
+ "dagger2",
"androidx.test.uiautomator_uiautomator",
"androidx.core_core-animation-testing",
"androidx.test.ext.junit",
@@ -618,6 +635,9 @@ android_robolectric_test {
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
+ plugins: [
+ "dagger2-compiler",
+ ],
}
// Opt-out config for optimizing the SystemUI target using R8.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 58816310d495..e218308758ab 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -982,6 +982,24 @@
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
+ <activity android:name="com.android.systemui.communal.widgets.WidgetPickerActivity"
+ android:theme="@style/Theme.EditWidgetsActivity"
+ android:excludeFromRecents="true"
+ android:autoRemoveFromRecents="true"
+ android:showOnLockScreen="true"
+ android:launchMode="singleTop"
+ android:exported="false">
+ </activity>
+
+ <activity android:name="com.android.systemui.communal.widgets.EditWidgetsActivity"
+ android:theme="@style/Theme.EditWidgetsActivity"
+ android:excludeFromRecents="true"
+ android:autoRemoveFromRecents="true"
+ android:showOnLockScreen="true"
+ android:launchMode="singleTop"
+ android:exported="false">
+ </activity>
+
<!-- Doze with notifications, run in main sysui process for every user -->
<service
android:name=".doze.DozeService"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 34545cf190f3..967a36b38090 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -27,6 +27,8 @@ cameronyee@google.com
chandruis@google.com
chrisgollner@google.com
cinek@google.com
+cocod@google.com
+darrellshi@google.com
dupin@google.com
ethibodeau@google.com
evanlaird@google.com
@@ -80,6 +82,7 @@ petrcermak@google.com
pinyaoting@google.com
pixel@google.com
pomini@google.com
+princedonkor@google.com
rahulbanerjee@google.com
roosa@google.com
saff@google.com
@@ -102,6 +105,7 @@ vanjan@google.com
victortulias@google.com
winsonc@google.com
wleshner@google.com
+wxyz@google.com
xilei@google.com
xuqiu@google.com
yeinj@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8f350a7ecb3e..a745ab5cbdd9 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -38,6 +38,13 @@ flag {
}
flag {
+ name: "notifications_hide_on_display_switch"
+ namespace: "systemui"
+ description: "Temporary hides notifications when folding/unfolding to reduce unfold latency"
+ bug: "293824309"
+}
+
+flag {
name: "notification_lifetime_extension_refactor"
namespace: "systemui"
description: "Enables moving notification lifetime extension management from SystemUI to "
@@ -54,6 +61,13 @@ flag {
}
flag {
+ name: "notification_throttle_hun"
+ namespace: "systemui"
+ description: "During notification avalanche, throttle HUNs showing in fast succession."
+ bug: "307288824"
+}
+
+flag {
name: "scene_container"
namespace: "systemui"
description: "Enables the scene container framework go/flexiglass."
@@ -98,3 +112,39 @@ flag {
"keyguard_root_view."
bug: "278054201"
}
+
+flag {
+ name: "qs_new_pipeline"
+ namespace: "systemui"
+ description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
+ bug: "241772429"
+}
+
+flag {
+ name: "coroutine_tracing"
+ namespace: "systemui"
+ description: "Adds thread-local data to System UI's global coroutine scopes to "
+ "allow for tracing of coroutine continuations using System UI's tracinglib"
+ bug: "289353932"
+}
+
+flag {
+ name: "new_aod_transition"
+ namespace: "systemui"
+ description: "New LOCKSCREEN <=> AOD transition"
+ bug: "301915812"
+}
+
+flag {
+ name: "light_reveal_migration"
+ namespace: "systemui"
+ description: "Move LightRevealScrim to recommended architecture"
+ bug: "281655028"
+}
+
+flag {
+ name: "media_in_scene_container"
+ namespace: "systemui"
+ description: "Enable media in the scene container framework"
+ bug: "296122467"
+}
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 ddd1c67bd5fa..914e5f2c17bf 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
@@ -22,7 +22,7 @@ import android.view.View
import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.scene.shared.model.Scene
@@ -47,6 +47,14 @@ object ComposeFacade : BaseComposeFacade {
throwComposeUnavailableError()
}
+ override fun setCommunalEditWidgetActivityContent(
+ activity: ComponentActivity,
+ viewModel: BaseCommunalViewModel,
+ onOpenWidgetPicker: () -> Unit,
+ ) {
+ throwComposeUnavailableError()
+ }
+
override fun createFooterActionsView(
context: Context,
viewModel: FooterActionsViewModel,
@@ -67,12 +75,12 @@ object ComposeFacade : BaseComposeFacade {
override fun createCommunalView(
context: Context,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
): View {
throwComposeUnavailableError()
}
- override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
+ override fun createCommunalContainer(context: Context, viewModel: BaseCommunalViewModel): View {
throwComposeUnavailableError()
}
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 eeda6c63b68f..59bd95bd9027 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
@@ -32,7 +32,7 @@ import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalHub
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -62,6 +62,21 @@ object ComposeFacade : BaseComposeFacade {
activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
}
+ override fun setCommunalEditWidgetActivityContent(
+ activity: ComponentActivity,
+ viewModel: BaseCommunalViewModel,
+ onOpenWidgetPicker: () -> Unit,
+ ) {
+ activity.setContent {
+ PlatformTheme {
+ CommunalHub(
+ viewModel = viewModel,
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ )
+ }
+ }
+ }
+
override fun createFooterActionsView(
context: Context,
viewModel: FooterActionsViewModel,
@@ -98,14 +113,14 @@ object ComposeFacade : BaseComposeFacade {
override fun createCommunalView(
context: Context,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
): View {
return ComposeView(context).apply {
setContent { PlatformTheme { CommunalHub(viewModel = viewModel) } }
}
}
- override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
+ override fun createCommunalContainer(context: Context, viewModel: BaseCommunalViewModel): View {
return ComposeView(context).apply {
setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 4400786fe9c3..57af2ba59566 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,7 +19,6 @@ package com.android.systemui.bouncer.ui.composable
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
-import android.content.res.Configuration
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
@@ -39,6 +38,7 @@ 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.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -53,8 +53,6 @@ import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
-import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -67,7 +65,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow
@@ -77,10 +74,13 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import com.android.compose.PlatformButton
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneKey as SceneTransitionLayoutSceneKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.transitions
import com.android.compose.modifiers.thenIf
-import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -89,6 +89,8 @@ import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.fold.ui.composable.foldPosture
+import com.android.systemui.fold.ui.helper.FoldPosture
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -143,10 +145,7 @@ private fun SceneScope.BouncerScene(
) {
val backgroundColor = MaterialTheme.colorScheme.surface
val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
- val layout =
- calculateLayout(
- isSideBySideSupported = isSideBySideSupported,
- )
+ val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
@@ -157,29 +156,28 @@ private fun SceneScope.BouncerScene(
val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
when (layout) {
- Layout.STANDARD ->
- Bouncer(
+ BouncerSceneLayout.STANDARD ->
+ StandardLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
- userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = childModifier,
)
- Layout.SIDE_BY_SIDE ->
- SideBySide(
+ BouncerSceneLayout.SIDE_BY_SIDE ->
+ SideBySideLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- Layout.STACKED ->
- Stacked(
+ BouncerSceneLayout.STACKED ->
+ StackedLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- Layout.SPLIT ->
- Split(
+ BouncerSceneLayout.SPLIT ->
+ SplitLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
modifier = childModifier,
@@ -193,43 +191,146 @@ private fun SceneScope.BouncerScene(
* authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
*/
@Composable
-private fun Bouncer(
+private fun StandardLayout(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
- userInputAreaVisibility: UserInputAreaVisibility,
modifier: Modifier = Modifier,
+ outputOnly: Boolean = false,
) {
- val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
- var dialog: Dialog? by remember { mutableStateOf(null) }
- val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
-
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(60.dp),
- modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 92.dp)
+ val foldPosture: FoldPosture by foldPosture()
+ val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+ val isSplitAroundTheFold =
+ foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
+ val currentSceneKey =
+ if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
+
+ SceneTransitionLayout(
+ currentScene = currentSceneKey,
+ onChangeScene = {},
+ transitions = SceneTransitions,
+ modifier = modifier,
) {
- Crossfade(
- targetState = message,
- label = "Bouncer message",
- animationSpec = if (message.isUpdateAnimated) tween() else snap(),
- ) { message ->
- Text(
- text = message.text,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyLarge,
+ scene(SceneKeys.ContiguousSceneKey) {
+ FoldSplittable(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ outputOnly = outputOnly,
+ isSplit = false,
)
}
- Box(Modifier.weight(1f)) {
- UserInputArea(
+ scene(SceneKeys.SplitSceneKey) {
+ FoldSplittable(
viewModel = viewModel,
- visibility = userInputAreaVisibility,
- modifier = Modifier.align(Alignment.Center),
+ dialogFactory = dialogFactory,
+ outputOnly = outputOnly,
+ isSplit = true,
)
}
+ }
+}
- actionButton?.let { BouncerActionButton(viewModel = it) }
+/**
+ * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
+ * switcher UI) and laid out vertically, centered horizontally.
+ *
+ * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
+ * render across the location of the fold hardware when the device is fully or part-way unfolded
+ * with the fold hinge in a horizontal position.
+ *
+ * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
+ * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
+ * their PIN or pattern.
+ */
+@Composable
+private fun SceneScope.FoldSplittable(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerSceneDialogFactory,
+ outputOnly: Boolean,
+ isSplit: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
+ val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+ var dialog: Dialog? by remember { mutableStateOf(null) }
+ val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+ val splitRatio =
+ LocalContext.current.resources.getFloat(
+ R.dimen.motion_layout_half_fold_bouncer_height_ratio
+ )
+
+ Column(modifier = modifier.padding(horizontal = 32.dp)) {
+ // Content above the fold, when split on a foldable device in a "table top" posture:
+ Box(
+ modifier =
+ Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
+ Modifier.weight(splitRatio)
+ },
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
+ ) {
+ Crossfade(
+ targetState = message,
+ label = "Bouncer message",
+ animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+ ) { message ->
+ Text(
+ text = message.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
+
+ Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = UserInputAreaVisibility.OUTPUT_ONLY,
+ )
+ }
+ }
+
+ // Content below the fold, when split on a foldable device in a "table top" posture:
+ Box(
+ modifier =
+ Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
+ Modifier.weight(1 - splitRatio)
+ },
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ if (!outputOnly) {
+ Box(Modifier.weight(1f)) {
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = UserInputAreaVisibility.INPUT_ONLY,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+
+ Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+ val actionButtonModifier = Modifier.height(56.dp)
+
+ actionButton.let { actionButtonViewModel ->
+ if (actionButtonViewModel != null) {
+ BouncerActionButton(
+ viewModel = actionButtonViewModel,
+ modifier = actionButtonModifier,
+ )
+ } else {
+ Spacer(modifier = actionButtonModifier)
+ }
+ }
+
+ Spacer(Modifier.height(48.dp))
+ }
+ }
if (dialogMessage != null) {
if (dialog == null) {
@@ -271,8 +372,8 @@ private fun UserInputArea(
when (val nonNullViewModel = authMethodViewModel) {
is PinBouncerViewModel ->
when (visibility) {
- UserInputAreaVisibility.FULL ->
- PinBouncer(
+ UserInputAreaVisibility.OUTPUT_ONLY ->
+ PinInputDisplay(
viewModel = nonNullViewModel,
modifier = modifier,
)
@@ -281,34 +382,20 @@ private fun UserInputArea(
viewModel = nonNullViewModel,
modifier = modifier,
)
- UserInputAreaVisibility.OUTPUT_ONLY ->
- PinInputDisplay(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
- UserInputAreaVisibility.NONE -> {}
}
is PasswordBouncerViewModel ->
- when (visibility) {
- UserInputAreaVisibility.FULL,
- UserInputAreaVisibility.INPUT_ONLY ->
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
- else -> {}
+ if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
}
is PatternBouncerViewModel ->
- when (visibility) {
- UserInputAreaVisibility.FULL,
- UserInputAreaVisibility.INPUT_ONLY ->
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
- .then(modifier)
- )
- else -> {}
+ if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+ )
}
else -> Unit
}
@@ -475,17 +562,17 @@ private fun UserSwitcherDropdownMenu(
* by double-tapping on the side.
*/
@Composable
-private fun Split(
+private fun SplitLayout(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
modifier: Modifier = Modifier,
) {
SwappableLayout(
startContent = { startContentModifier ->
- Bouncer(
+ StandardLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
- userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY,
+ outputOnly = true,
modifier = startContentModifier,
)
},
@@ -578,7 +665,7 @@ private fun SwappableLayout(
* rendering of the bouncer will be used instead of the side-by-side layout.
*/
@Composable
-private fun SideBySide(
+private fun SideBySideLayout(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
isUserSwitcherVisible: Boolean,
@@ -598,10 +685,9 @@ private fun SideBySide(
}
},
endContent = { endContentModifier ->
- Bouncer(
+ StandardLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
- userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = endContentModifier,
)
},
@@ -611,7 +697,7 @@ private fun SideBySide(
/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
@Composable
-private fun Stacked(
+private fun StackedLayout(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
isUserSwitcherVisible: Boolean,
@@ -627,75 +713,21 @@ private fun Stacked(
)
}
- Bouncer(
+ StandardLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
- userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = Modifier.fillMaxWidth().weight(1f),
)
}
}
-@Composable
-private fun calculateLayout(
- isSideBySideSupported: Boolean,
-): Layout {
- val windowSizeClass = LocalWindowSizeClass.current
- val width = windowSizeClass.widthSizeClass
- val height = windowSizeClass.heightSizeClass
- val isLarge = width > WindowWidthSizeClass.Compact && height > WindowHeightSizeClass.Compact
- val isTall =
- when (height) {
- WindowHeightSizeClass.Expanded -> width < WindowWidthSizeClass.Expanded
- WindowHeightSizeClass.Medium -> width < WindowWidthSizeClass.Medium
- else -> false
- }
- val isSquare =
- when (width) {
- WindowWidthSizeClass.Compact -> height == WindowHeightSizeClass.Compact
- WindowWidthSizeClass.Medium -> height == WindowHeightSizeClass.Medium
- WindowWidthSizeClass.Expanded -> height == WindowHeightSizeClass.Expanded
- else -> false
- }
- val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
-
- return when {
- // Small and tall devices (i.e. phone/folded in portrait) or square device not in landscape
- // mode (unfolded with hinge along horizontal plane).
- (!isLarge && isTall) || (isSquare && !isLandscape) -> Layout.STANDARD
- // Small and wide devices (i.e. phone/folded in landscape).
- !isLarge -> Layout.SPLIT
- // Large and tall devices (i.e. tablet in portrait).
- isTall -> Layout.STACKED
- // Large and wide/square devices (i.e. tablet in landscape, unfolded).
- else -> if (isSideBySideSupported) Layout.SIDE_BY_SIDE else Layout.STANDARD
- }
-}
-
interface BouncerSceneDialogFactory {
operator fun invoke(): AlertDialog
}
-/** Enumerates all known adaptive layout configurations. */
-private enum class Layout {
- /** The default UI with the bouncer laid out normally. */
- STANDARD,
- /** The bouncer is displayed vertically stacked with the user switcher. */
- STACKED,
- /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
- SIDE_BY_SIDE,
- /** The bouncer is split in two with both sides shown side-by-side. */
- SPLIT,
-}
-
/** Enumerates all supported user-input area visibilities. */
private enum class UserInputAreaVisibility {
/**
- * The entire user input area is shown, including where the user enters input and where it's
- * reflected to the user.
- */
- FULL,
- /**
* Only the area where the user enters the input is shown; the area where the input is reflected
* back to the user is not shown.
*/
@@ -705,8 +737,6 @@ private enum class UserInputAreaVisibility {
* input is entered by the user is not shown.
*/
OUTPUT_ONLY,
- /** The entire user input area is hidden. */
- NONE,
}
/**
@@ -741,3 +771,17 @@ private fun animatedAlpha(
private val SelectedUserImageSize = 190.dp
private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
private val UserSwitcherDropdownHeight = 60.dp
+
+private object SceneKeys {
+ val ContiguousSceneKey = SceneTransitionLayoutSceneKey("default")
+ val SplitSceneKey = SceneTransitionLayoutSceneKey("split")
+}
+
+private object SceneElements {
+ val AboveFold = ElementKey("above_fold")
+ val BelowFold = ElementKey("below_fold")
+}
+
+private val SceneTransitions = transitions {
+ from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
new file mode 100644
index 000000000000..08b7559dcf97
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.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.bouncer.ui.composable
+
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
+import androidx.compose.runtime.Composable
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
+import com.android.systemui.bouncer.ui.helper.SizeClass
+import com.android.systemui.bouncer.ui.helper.calculateLayoutInternal
+
+/**
+ * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
+ * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.SIDE_BY_SIDE] is replaced by
+ * [BouncerSceneLayout.STANDARD].
+ */
+@Composable
+fun calculateLayout(
+ isSideBySideSupported: Boolean,
+): BouncerSceneLayout {
+ val windowSizeClass = LocalWindowSizeClass.current
+
+ return calculateLayoutInternal(
+ width = windowSizeClass.widthSizeClass.toEnum(),
+ height = windowSizeClass.heightSizeClass.toEnum(),
+ isSideBySideSupported = isSideBySideSupported,
+ )
+}
+
+private fun WindowWidthSizeClass.toEnum(): SizeClass {
+ return when (this) {
+ WindowWidthSizeClass.Compact -> SizeClass.COMPACT
+ WindowWidthSizeClass.Medium -> SizeClass.MEDIUM
+ WindowWidthSizeClass.Expanded -> SizeClass.EXPANDED
+ else -> error("Unsupported WindowWidthSizeClass \"$this\"")
+ }
+}
+
+private fun WindowHeightSizeClass.toEnum(): SizeClass {
+ return when (this) {
+ WindowHeightSizeClass.Compact -> SizeClass.COMPACT
+ WindowHeightSizeClass.Medium -> SizeClass.MEDIUM
+ WindowHeightSizeClass.Expanded -> SizeClass.EXPANDED
+ else -> error("Unsupported WindowHeightSizeClass \"$this\"")
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index df22a7023ebf..0b1338305076 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -28,6 +28,7 @@ import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -63,11 +64,13 @@ internal fun PasswordBouncer(
val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
- LaunchedEffect(Unit) {
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+
// When the UI comes up, request focus on the TextField to bring up the software keyboard.
focusRequester.requestFocus()
- // Also, report that the UI is shown to let the view-model runs some logic.
- viewModel.onShown()
+
+ onDispose { viewModel.onHidden() }
}
LaunchedEffect(animateFailure) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 03efbe0fe1ff..ff1cbd6b04c3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -26,6 +26,7 @@ import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -38,11 +39,11 @@ import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.modifiers.thenIf
@@ -65,8 +66,10 @@ internal fun PatternBouncer(
viewModel: PatternBouncerViewModel,
modifier: Modifier = Modifier,
) {
- // Report that the UI is shown to let the view-model run some logic.
- LaunchedEffect(Unit) { viewModel.onShown() }
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+ onDispose { viewModel.onHidden() }
+ }
val colCount = viewModel.columnCount
val rowCount = viewModel.rowCount
@@ -76,12 +79,6 @@ internal fun PatternBouncer(
val lineColor = MaterialTheme.colorScheme.primary
val lineStrokeWidth = with(LocalDensity.current) { LINE_STROKE_WIDTH_DP.dp.toPx() }
- var containerSize: IntSize by remember { mutableStateOf(IntSize(0, 0)) }
- val horizontalSpacing = containerSize.width / colCount
- val verticalSpacing = containerSize.height / rowCount
- val spacing = min(horizontalSpacing, verticalSpacing).toFloat()
- val verticalOffset = containerSize.height - spacing * rowCount
-
// All dots that should be rendered on the grid.
val dots: List<PatternDotViewModel> by viewModel.dots.collectAsState()
// The most recently selected dot, if the user is currently dragging.
@@ -192,13 +189,14 @@ internal fun PatternBouncer(
// This is the position of the input pointer.
var inputPosition: Offset? by remember { mutableStateOf(null) }
+ var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
Canvas(
modifier
// Need to clip to bounds to make sure that the lines don't follow the input pointer
// when it leaves the bounds of the dot grid.
.clipToBounds()
- .onSizeChanged { containerSize = it }
+ .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
.thenIf(isInputEnabled) {
Modifier.pointerInput(Unit) {
awaitEachGesture {
@@ -229,62 +227,72 @@ internal fun PatternBouncer(
viewModel.onDrag(
xPx = change.position.x,
yPx = change.position.y,
- containerSizePx = containerSize.width,
- verticalOffsetPx = verticalOffset,
+ containerSizePx = size.width,
)
}
}
}
) {
- if (isAnimationEnabled) {
- // Draw lines between dots.
- selectedDots.forEachIndexed { index, dot ->
- if (index > 0) {
- val previousDot = selectedDots[index - 1]
- val lineFadeOutAnimationProgress = lineFadeOutAnimatables[previousDot]!!.value
- val startLerp = 1 - lineFadeOutAnimationProgress
- val from = pixelOffset(previousDot, spacing, verticalOffset)
- val to = pixelOffset(dot, spacing, verticalOffset)
- val lerpedFrom =
- Offset(
- x = from.x + (to.x - from.x) * startLerp,
- y = from.y + (to.y - from.y) * startLerp,
+ gridCoordinates?.let { nonNullCoordinates ->
+ val containerSize = nonNullCoordinates.size
+ val horizontalSpacing = containerSize.width.toFloat() / colCount
+ val verticalSpacing = containerSize.height.toFloat() / rowCount
+ val spacing = min(horizontalSpacing, verticalSpacing)
+ val verticalOffset = containerSize.height - spacing * rowCount
+
+ if (isAnimationEnabled) {
+ // Draw lines between dots.
+ selectedDots.forEachIndexed { index, dot ->
+ if (index > 0) {
+ val previousDot = selectedDots[index - 1]
+ val lineFadeOutAnimationProgress =
+ lineFadeOutAnimatables[previousDot]!!.value
+ val startLerp = 1 - lineFadeOutAnimationProgress
+ val from = pixelOffset(previousDot, spacing, verticalOffset)
+ val to = pixelOffset(dot, spacing, verticalOffset)
+ val lerpedFrom =
+ Offset(
+ x = from.x + (to.x - from.x) * startLerp,
+ y = from.y + (to.y - from.y) * startLerp,
+ )
+ drawLine(
+ start = lerpedFrom,
+ end = to,
+ cap = StrokeCap.Round,
+ alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
)
- drawLine(
- start = lerpedFrom,
- end = to,
- cap = StrokeCap.Round,
- alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ }
}
- }
- // Draw the line between the most recently-selected dot and the input pointer position.
- inputPosition?.let { lineEnd ->
- currentDot?.let { dot ->
- val from = pixelOffset(dot, spacing, verticalOffset)
- val lineLength = sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
- drawLine(
- start = from,
- end = lineEnd,
- cap = StrokeCap.Round,
- alpha = lineAlpha(spacing, lineLength),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ // Draw the line between the most recently-selected dot and the input pointer
+ // position.
+ inputPosition?.let { lineEnd ->
+ currentDot?.let { dot ->
+ val from = pixelOffset(dot, spacing, verticalOffset)
+ val lineLength =
+ sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
+ drawLine(
+ start = from,
+ end = lineEnd,
+ cap = StrokeCap.Round,
+ alpha = lineAlpha(spacing, lineLength),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
+ )
+ }
}
}
- }
- // Draw each dot on the grid.
- dots.forEach { dot ->
- drawCircle(
- center = pixelOffset(dot, spacing, verticalOffset),
- color = dotColor,
- radius = dotRadius * (dotScalingAnimatables[dot]?.value ?: 1f),
- )
+ // Draw each dot on the grid.
+ dots.forEach { dot ->
+ drawCircle(
+ center = pixelOffset(dot, spacing, verticalOffset),
+ color = dotColor,
+ radius = dotRadius * (dotScalingAnimatables[dot]?.value ?: 1f),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 84e016791816..59617c9022ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -24,17 +24,14 @@ import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -69,38 +66,21 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
-internal fun PinBouncer(
+fun PinPad(
viewModel: PinBouncerViewModel,
modifier: Modifier = Modifier,
) {
- // Report that the UI is shown to let the view-model run some logic.
- LaunchedEffect(Unit) { viewModel.onShown() }
-
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier =
- modifier.pointerInput(Unit) {
- awaitEachGesture {
- awaitFirstDown()
- viewModel.onDown()
- }
- }
- ) {
- PinInputDisplay(viewModel)
- Spacer(Modifier.height(100.dp))
- PinPad(viewModel)
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+ onDispose { viewModel.onHidden() }
}
-}
-@Composable
-fun PinPad(
- viewModel: PinBouncerViewModel,
- modifier: Modifier = Modifier,
-) {
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+ val isDigitButtonAnimationEnabled: Boolean by
+ viewModel.isDigitButtonAnimationEnabled.collectAsState()
val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
LaunchedEffect(animateFailure) {
@@ -119,10 +99,11 @@ fun PinPad(
) {
repeat(9) { index ->
DigitButton(
- index + 1,
- isInputEnabled,
- viewModel::onPinButtonClicked,
- buttonScaleAnimatables[index]::value,
+ digit = index + 1,
+ isInputEnabled = isInputEnabled,
+ onClicked = viewModel::onPinButtonClicked,
+ scaling = buttonScaleAnimatables[index]::value,
+ isAnimationEnabled = isDigitButtonAnimationEnabled,
)
}
@@ -141,10 +122,11 @@ fun PinPad(
)
DigitButton(
- 0,
- isInputEnabled,
- viewModel::onPinButtonClicked,
- buttonScaleAnimatables[10]::value,
+ digit = 0,
+ isInputEnabled = isInputEnabled,
+ onClicked = viewModel::onPinButtonClicked,
+ scaling = buttonScaleAnimatables[10]::value,
+ isAnimationEnabled = isDigitButtonAnimationEnabled,
)
ActionButton(
@@ -168,15 +150,17 @@ private fun DigitButton(
isInputEnabled: Boolean,
onClicked: (Int) -> Unit,
scaling: () -> Float,
+ isAnimationEnabled: Boolean,
) {
PinPadButton(
onClicked = { onClicked(digit) },
isEnabled = isInputEnabled,
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ isAnimationEnabled = isAnimationEnabled,
modifier =
Modifier.graphicsLayer {
- val scale = scaling()
+ val scale = if (isAnimationEnabled) scaling() else 1f
scaleX = scale
scaleY = scale
}
@@ -220,6 +204,7 @@ private fun ActionButton(
isEnabled = isInputEnabled && !isHidden,
backgroundColor = backgroundColor,
foregroundColor = foregroundColor,
+ isAnimationEnabled = true,
modifier =
Modifier.graphicsLayer {
alpha = hiddenAlpha
@@ -241,6 +226,7 @@ private fun PinPadButton(
isEnabled: Boolean,
backgroundColor: Color,
foregroundColor: Color,
+ isAnimationEnabled: Boolean,
modifier: Modifier = Modifier,
onLongPressed: (() -> Unit)? = null,
content: @Composable (contentColor: () -> Color) -> Unit,
@@ -268,7 +254,7 @@ private fun PinPadButton(
val cornerRadius: Dp by
animateDpAsState(
- if (isPressed) 24.dp else pinButtonSize / 2,
+ if (isAnimationEnabled && isPressed) 24.dp else pinButtonSize / 2,
label = "PinButton round corners",
animationSpec = tween(animDurationMillis, easing = animEasing)
)
@@ -276,7 +262,7 @@ private fun PinPadButton(
val containerColor: Color by
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.primary
+ isAnimationEnabled && isPressed -> MaterialTheme.colorScheme.primary
else -> backgroundColor
},
label = "Pin button container color",
@@ -285,7 +271,7 @@ private fun PinPadButton(
val contentColor =
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.onPrimary
+ isAnimationEnabled && isPressed -> MaterialTheme.colorScheme.onPrimary
else -> foregroundColor
},
label = "Pin button container color",
@@ -298,7 +284,8 @@ private fun PinPadButton(
contentAlignment = Alignment.Center,
modifier =
modifier
- .size(pinButtonSize)
+ .sizeIn(maxWidth = pinButtonSize, maxHeight = pinButtonSize)
+ .aspectRatio(1f)
.drawBehind {
drawRoundRect(
color = containerColor,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 814ea31ad510..1a97912c77bb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -18,6 +18,11 @@
package com.android.systemui.bouncer.ui.composable
+import android.app.AlertDialog
+import android.app.Dialog
+import android.view.Gravity
+import android.view.WindowManager
+import android.widget.TextView
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.tween
@@ -26,11 +31,16 @@ import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -41,14 +51,21 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.toMutableStateList
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
@@ -189,6 +206,10 @@ private fun RegularPinInputDisplay(
shapeAnimations: ShapeAnimations,
modifier: Modifier = Modifier,
) {
+ if (viewModel.isSimAreaVisible) {
+ SimArea(viewModel = viewModel)
+ }
+
// Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from
// `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove
// animation, thus the composable to be removed has to remain in the composition until fully
@@ -234,6 +255,94 @@ private fun RegularPinInputDisplay(
pinInputRow.Content(modifier)
}
+@Composable
+private fun SimArea(viewModel: PinBouncerViewModel) {
+ val isLockedEsim by viewModel.isLockedEsim.collectAsState()
+ val isSimUnlockingDialogVisible by viewModel.isSimUnlockingDialogVisible.collectAsState()
+ val errorDialogMessage by viewModel.errorDialogMessage.collectAsState()
+ var unlockDialog: Dialog? by remember { mutableStateOf(null) }
+ var errorDialog: Dialog? by remember { mutableStateOf(null) }
+ val context = LocalView.current.context
+
+ DisposableEffect(isSimUnlockingDialogVisible) {
+ if (isSimUnlockingDialogVisible) {
+ val builder =
+ AlertDialog.Builder(context).apply {
+ setMessage(context.getString(R.string.kg_sim_unlock_progress_dialog_message))
+ setCancelable(false)
+ }
+ unlockDialog =
+ builder.create().apply {
+ window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ show()
+ findViewById<TextView>(android.R.id.message)?.gravity = Gravity.CENTER
+ }
+ } else {
+ unlockDialog?.hide()
+ unlockDialog = null
+ }
+
+ onDispose {
+ unlockDialog?.hide()
+ unlockDialog = null
+ }
+ }
+
+ DisposableEffect(errorDialogMessage) {
+ if (errorDialogMessage != null) {
+ val builder = AlertDialog.Builder(context)
+ builder.setMessage(errorDialogMessage)
+ builder.setCancelable(false)
+ builder.setNeutralButton(R.string.ok, null)
+ errorDialog =
+ builder.create().apply {
+ window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ setOnDismissListener { viewModel.onErrorDialogDismissed() }
+ show()
+ }
+ } else {
+ errorDialog?.hide()
+ errorDialog = null
+ }
+
+ onDispose {
+ errorDialog?.hide()
+ errorDialog = null
+ }
+ }
+
+ Box(modifier = Modifier.padding(bottom = 20.dp)) {
+ // If isLockedEsim is null, then we do not show anything.
+ if (isLockedEsim == true) {
+ PlatformOutlinedButton(
+ onClick = { viewModel.onDisableEsimButtonClicked() },
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_no_sim),
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface)
+ )
+ Text(
+ text = stringResource(R.string.disable_carrier_button_text),
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ }
+ } else if (isLockedEsim == false) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_lockscreen_sim),
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected))
+ )
+ }
+ }
+}
+
private class PinInputRow(
val shapeAnimations: ShapeAnimations,
) {
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 37804682ed98..2c4dc806f468 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
@@ -12,7 +12,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -25,13 +24,15 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import kotlinx.coroutines.flow.transform
object Communal {
@@ -58,16 +59,19 @@ val sceneTransitions = transitions {
@Composable
fun CommunalContainer(
modifier: Modifier = Modifier,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
) {
val currentScene: SceneKey by
viewModel.currentScene
.transform<CommunalSceneKey, SceneKey> { value -> value.toTransitionSceneKey() }
.collectAsState(TransitionSceneKey.Blank)
+ // Don't show hub mode UI if keyguard is present. This is important since we're in the shade,
+ // which can be opened from many locations.
+ val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false)
// Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
var showSceneTransitionLayout by remember { mutableStateOf(true) }
- if (!showSceneTransitionLayout) {
+ if (!showSceneTransitionLayout || !isKeyguardShowing) {
return
}
@@ -76,17 +80,24 @@ fun CommunalContainer(
currentScene = currentScene,
onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) },
transitions = sceneTransitions,
+ edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize)
) {
scene(
TransitionSceneKey.Blank,
- userActions = mapOf(Swipe.Left to TransitionSceneKey.Communal)
+ userActions =
+ mapOf(
+ Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal
+ )
) {
BlankScene { showSceneTransitionLayout = false }
}
scene(
TransitionSceneKey.Communal,
- userActions = mapOf(Swipe.Right to TransitionSceneKey.Blank),
+ userActions =
+ mapOf(
+ Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank
+ ),
) {
CommunalScene(viewModel, modifier = modifier)
}
@@ -105,14 +116,12 @@ private fun BlankScene(
Box(modifier.fillMaxSize()) {
Column(
Modifier.fillMaxHeight()
- .width(100.dp)
+ .width(ContainerDimensions.EdgeSwipeSize)
.align(Alignment.CenterEnd)
.background(Color(0x55e9f2eb)),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text("Default scene")
-
IconButton(onClick = hideSceneTransitionLayout) {
Icon(Icons.Filled.Close, contentDescription = "Close button")
}
@@ -123,7 +132,7 @@ private fun BlankScene(
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
modifier: Modifier = Modifier,
) {
Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
@@ -142,3 +151,7 @@ fun CommunalSceneKey.toTransitionSceneKey(): SceneKey {
fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
return this.identity as CommunalSceneKey
}
+
+object ContainerDimensions {
+ val EdgeSwipeSize = 40.dp
+}
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 142978284cfb..e8ecd3a66186 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
@@ -19,21 +19,27 @@ package com.android.systemui.communal.ui.compose
import android.os.Bundle
import android.util.SizeF
import android.widget.FrameLayout
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
@@ -42,13 +48,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.res.R
@@ -56,55 +64,115 @@ import com.android.systemui.res.R
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
+ onOpenWidgetPicker: (() -> Unit)? = null,
) {
val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
Box(
modifier = modifier.fillMaxSize().background(Color.White),
) {
- LazyHorizontalGrid(
- modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
- rows = GridCells.Fixed(CommunalContentSize.FULL.span),
- horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
- verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
- ) {
- items(
- count = communalContent.size,
- key = { index -> communalContent[index].key },
- span = { index -> GridItemSpan(communalContent[index].size.span) },
- ) { index ->
+ CommunalHubLazyGrid(
+ modifier = Modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
+ communalContent = communalContent,
+ isEditMode = viewModel.isEditMode,
+ viewModel = viewModel,
+ )
+ if (viewModel.isEditMode && onOpenWidgetPicker != null) {
+ IconButton(onClick = onOpenWidgetPicker) {
+ Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
+ }
+ } else {
+ IconButton(onClick = viewModel::onOpenWidgetEditor) {
+ Icon(Icons.Default.Edit, stringResource(R.string.button_to_open_widget_editor))
+ }
+ }
+
+ // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
+ // touches, so that the SceneTransitionLayout can intercept the touches and allow an edge
+ // swipe back to the blank scene.
+ Spacer(
+ Modifier.height(Dimensions.GridHeight)
+ .align(Alignment.CenterStart)
+ .width(Dimensions.Spacing)
+ .pointerInput(Unit) {}
+ )
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun CommunalHubLazyGrid(
+ communalContent: List<CommunalContentModel>,
+ isEditMode: Boolean,
+ viewModel: BaseCommunalViewModel,
+ modifier: Modifier = Modifier,
+) {
+ var gridModifier = modifier
+ val gridState = rememberLazyGridState()
+ var list = communalContent
+ var dragDropState: GridDragDropState? = null
+ if (isEditMode && viewModel is CommunalEditModeViewModel) {
+ val contentListState = rememberContentListState(communalContent, viewModel)
+ list = contentListState.list
+ dragDropState = rememberGridDragDropState(gridState, contentListState)
+ gridModifier = gridModifier.dragContainer(dragDropState)
+ }
+ LazyHorizontalGrid(
+ modifier = gridModifier,
+ state = gridState,
+ rows = GridCells.Fixed(CommunalContentSize.FULL.span),
+ contentPadding = PaddingValues(horizontal = Dimensions.Spacing),
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+ verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+ ) {
+ items(
+ count = list.size,
+ key = { index -> list[index].key },
+ span = { index -> GridItemSpan(list[index].size.span) },
+ ) { index ->
+ val cardModifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth)
+ val size =
+ SizeF(
+ Dimensions.CardWidth.value,
+ list[index].size.dp().value,
+ )
+ if (isEditMode && dragDropState != null) {
+ DraggableItem(dragDropState = dragDropState, enabled = true, index = index) {
+ isDragging ->
+ val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
+ CommunalContent(
+ modifier = cardModifier,
+ deleteOnClick = viewModel::onDeleteWidget,
+ elevation = elevation,
+ model = list[index],
+ viewModel = viewModel,
+ size = size,
+ )
+ }
+ } else {
CommunalContent(
- modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
- model = communalContent[index],
+ modifier = cardModifier,
+ model = list[index],
viewModel = viewModel,
- deleteOnClick = viewModel::onDeleteWidget,
- size =
- SizeF(
- Dimensions.CardWidth.value,
- communalContent[index].size.dp().value,
- ),
+ size = size,
)
}
}
- IconButton(onClick = viewModel::onOpenWidgetPicker) {
- Icon(
- Icons.Default.Add,
- LocalContext.current.getString(R.string.button_to_open_widget_picker)
- )
- }
}
}
@Composable
private fun CommunalContent(
model: CommunalContentModel,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
size: SizeF,
- deleteOnClick: (id: Int) -> Unit,
modifier: Modifier = Modifier,
+ elevation: Dp = 0.dp,
+ deleteOnClick: ((id: Int) -> Unit)? = null,
) {
when (model) {
- is CommunalContentModel.Widget -> WidgetContent(model, size, deleteOnClick, modifier)
+ is CommunalContentModel.Widget ->
+ WidgetContent(model, size, elevation, deleteOnClick, modifier)
is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, modifier)
@@ -115,18 +183,19 @@ private fun CommunalContent(
private fun WidgetContent(
model: CommunalContentModel.Widget,
size: SizeF,
- deleteOnClick: (id: Int) -> Unit,
+ elevation: Dp,
+ deleteOnClick: ((id: Int) -> Unit)?,
modifier: Modifier = Modifier,
) {
// TODO(b/309009246): update background color
- Box(
+ Card(
modifier = modifier.fillMaxSize().background(Color.White),
+ elevation = CardDefaults.cardElevation(draggedElevation = elevation),
) {
- IconButton(onClick = { deleteOnClick(model.appWidgetId) }) {
- Icon(
- Icons.Default.Close,
- LocalContext.current.getString(R.string.button_to_remove_widget)
- )
+ if (deleteOnClick != null) {
+ IconButton(onClick = { deleteOnClick(model.appWidgetId) }) {
+ Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
+ }
}
AndroidView(
modifier = modifier,
@@ -135,8 +204,6 @@ private fun WidgetContent(
.createView(context, model.appWidgetId, model.providerInfo)
.apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
},
- // For reusing composition in lazy lists.
- onReset = {}
)
}
}
@@ -162,13 +229,9 @@ private fun TutorialContent(modifier: Modifier = Modifier) {
}
@Composable
-private fun Umo(viewModel: CommunalViewModel, modifier: Modifier = Modifier) {
+private fun Umo(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) {
AndroidView(
- modifier =
- modifier
- .width(Dimensions.CardWidth)
- .height(Dimensions.CardHeightThird)
- .padding(Dimensions.Spacing),
+ modifier = modifier,
factory = {
viewModel.mediaHost.expansion = MediaHostState.EXPANDED
viewModel.mediaHost.showsOnlyActiveMedia = false
@@ -194,7 +257,7 @@ private fun CommunalContentSize.dp(): Dp {
}
}
-private object Dimensions {
+object Dimensions {
val CardWidth = 464.dp
val CardHeightFull = 630.dp
val CardHeightHalf = 307.dp
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
new file mode 100644
index 000000000000..89c57658b474
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.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.communal.ui.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+
+@Composable
+fun rememberContentListState(
+ communalContent: List<CommunalContentModel>,
+ viewModel: CommunalEditModeViewModel,
+): ContentListState {
+ return remember(communalContent) {
+ ContentListState(
+ communalContent,
+ viewModel::onDeleteWidget,
+ viewModel::onReorderWidgets,
+ )
+ }
+}
+
+/**
+ * Keeps the current state of the [CommunalContentModel] list being edited. [GridDragDropState]
+ * interacts with this class to update the order in the list. [onSaveList] should be called on
+ * dragging ends to persist the state in db for better performance.
+ */
+class ContentListState
+internal constructor(
+ communalContent: List<CommunalContentModel>,
+ private val onDeleteWidget: (id: Int) -> Unit,
+ private val onReorderWidgets: (ids: List<Int>) -> Unit,
+) {
+ var list by mutableStateOf(communalContent)
+ private set
+
+ /** Move item to a new position in the list. */
+ fun onMove(fromIndex: Int, toIndex: Int) {
+ list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) }
+ }
+
+ /** Remove widget from the list and the database. */
+ fun onRemove(indexToRemove: Int) {
+ if (list[indexToRemove] is CommunalContentModel.Widget) {
+ val widget = list[indexToRemove] as CommunalContentModel.Widget
+ list = list.toMutableList().apply { removeAt(indexToRemove) }
+ onDeleteWidget(widget.appWidgetId)
+ }
+ }
+
+ /** Persist the new order with all the movements happened during dragging. */
+ fun onSaveList() {
+ val widgetIds: List<Int> =
+ list.filterIsInstance<CommunalContentModel.Widget>().map { it.appWidgetId }
+ onReorderWidgets(widgetIds)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
new file mode 100644
index 000000000000..6cfa2f233f46
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -0,0 +1,247 @@
+/*
+ * 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.communal.ui.compose
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
+import androidx.compose.foundation.lazy.grid.LazyGridItemScope
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.toOffset
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.zIndex
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
+
+@Composable
+fun rememberGridDragDropState(
+ gridState: LazyGridState,
+ contentListState: ContentListState
+): GridDragDropState {
+ val scope = rememberCoroutineScope()
+ val state =
+ remember(gridState, contentListState) {
+ GridDragDropState(state = gridState, contentListState = contentListState, scope = scope)
+ }
+ LaunchedEffect(state) {
+ while (true) {
+ val diff = state.scrollChannel.receive()
+ gridState.scrollBy(diff)
+ }
+ }
+ return state
+}
+
+/**
+ * Handles drag and drop cards in the glanceable hub. While dragging to move, other items that are
+ * affected will dynamically get positioned and the state is tracked by [ContentListState]. When
+ * dragging to remove, affected cards will be moved and [ContentListState.onRemove] is called to
+ * remove the dragged item. On dragging ends, call [ContentListState.onSaveList] to persist the
+ * change.
+ */
+class GridDragDropState
+internal constructor(
+ private val state: LazyGridState,
+ private val contentListState: ContentListState,
+ private val scope: CoroutineScope,
+) {
+ var draggingItemIndex by mutableStateOf<Int?>(null)
+ private set
+
+ internal val scrollChannel = Channel<Float>()
+
+ private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
+ private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
+ internal val draggingItemOffset: Offset
+ get() =
+ draggingItemLayoutInfo?.let { item ->
+ draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
+ }
+ ?: Offset.Zero
+
+ private val draggingItemLayoutInfo: LazyGridItemInfo?
+ get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
+
+ internal fun onDragStart(offset: Offset) {
+ state.layoutInfo.visibleItemsInfo
+ .firstOrNull { item ->
+ item.isEditable &&
+ offset.x.toInt() in item.offset.x..item.offsetEnd.x &&
+ offset.y.toInt() in item.offset.y..item.offsetEnd.y
+ }
+ ?.apply {
+ draggingItemIndex = index
+ draggingItemInitialOffset = this.offset.toOffset()
+ }
+ }
+
+ internal fun onDragInterrupted() {
+ if (draggingItemIndex != null) {
+ // persist list editing changes on dragging ends
+ contentListState.onSaveList()
+ draggingItemIndex = null
+ }
+ draggingItemDraggedDelta = Offset.Zero
+ draggingItemInitialOffset = Offset.Zero
+ }
+
+ internal fun onDrag(offset: Offset) {
+ draggingItemDraggedDelta += offset
+
+ val draggingItem = draggingItemLayoutInfo ?: return
+ val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
+ val endOffset = startOffset + draggingItem.size.toSize()
+ val middleOffset = startOffset + (endOffset - startOffset) / 2f
+
+ val targetItem =
+ state.layoutInfo.visibleItemsInfo.find { item ->
+ item.isEditable &&
+ middleOffset.x.toInt() in item.offset.x..item.offsetEnd.x &&
+ middleOffset.y.toInt() in item.offset.y..item.offsetEnd.y &&
+ draggingItem.index != item.index
+ }
+
+ if (targetItem != null) {
+ val scrollToIndex =
+ if (targetItem.index == state.firstVisibleItemIndex) {
+ draggingItem.index
+ } else if (draggingItem.index == state.firstVisibleItemIndex) {
+ targetItem.index
+ } else {
+ null
+ }
+ if (scrollToIndex != null) {
+ scope.launch {
+ // this is needed to neutralize automatic keeping the first item first.
+ state.scrollToItem(scrollToIndex, state.firstVisibleItemScrollOffset)
+ contentListState.onMove(draggingItem.index, targetItem.index)
+ }
+ } else {
+ contentListState.onMove(draggingItem.index, targetItem.index)
+ }
+ draggingItemIndex = targetItem.index
+ } else {
+ val overscroll = checkForOverscroll(startOffset, endOffset)
+ if (overscroll != 0f) {
+ scrollChannel.trySend(overscroll)
+ }
+ val removeOffset = checkForRemove(startOffset)
+ if (removeOffset != 0f) {
+ draggingItemIndex?.let {
+ contentListState.onRemove(it)
+ draggingItemIndex = null
+ }
+ }
+ }
+ }
+
+ private val LazyGridItemInfo.offsetEnd: IntOffset
+ get() = this.offset + this.size
+
+ /** Whether the grid item can be dragged or be a drop target. Only widget card is editable. */
+ private val LazyGridItemInfo.isEditable: Boolean
+ get() = contentListState.list[this.index] is CommunalContentModel.Widget
+
+ /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
+ private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
+ return when {
+ draggingItemDraggedDelta.x > 0 ->
+ (endOffset.x - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
+ draggingItemDraggedDelta.x < 0 ->
+ (startOffset.x - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
+ else -> 0f
+ }
+ }
+
+ // TODO(b/309968801): a temporary solution to decide whether to remove card when it's dragged up
+ // and out of grid. Once we have a taskbar, calculate the intersection of the dragged item with
+ // the Remove button.
+ private fun checkForRemove(startOffset: Offset): Float {
+ return if (draggingItemDraggedDelta.y < 0)
+ (startOffset.y + Dimensions.CardHeightHalf.value - state.layoutInfo.viewportStartOffset)
+ .coerceAtMost(0f)
+ else 0f
+ }
+}
+
+private operator fun IntOffset.plus(size: IntSize): IntOffset {
+ return IntOffset(x + size.width, y + size.height)
+}
+
+private operator fun Offset.plus(size: Size): Offset {
+ return Offset(x + size.width, y + size.height)
+}
+
+fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier {
+ return pointerInput(dragDropState) {
+ detectDragGesturesAfterLongPress(
+ onDrag = { change, offset ->
+ change.consume()
+ dragDropState.onDrag(offset = offset)
+ },
+ onDragStart = { offset -> dragDropState.onDragStart(offset) },
+ onDragEnd = { dragDropState.onDragInterrupted() },
+ onDragCancel = { dragDropState.onDragInterrupted() }
+ )
+ }
+}
+
+/** Wrap LazyGrid item with additional modifier needed for drag and drop. */
+@ExperimentalFoundationApi
+@Composable
+fun LazyGridItemScope.DraggableItem(
+ dragDropState: GridDragDropState,
+ index: Int,
+ enabled: Boolean,
+ modifier: Modifier = Modifier,
+ content: @Composable (isDragging: Boolean) -> Unit
+) {
+ if (!enabled) {
+ return Box(modifier = modifier) { content(false) }
+ }
+ val dragging = index == dragDropState.draggingItemIndex
+ val draggingModifier =
+ if (dragging) {
+ Modifier.zIndex(1f).graphicsLayer {
+ translationX = dragDropState.draggingItemOffset.x
+ translationY = dragDropState.draggingItemOffset.y
+ }
+ } else {
+ Modifier.animateItemPlacement()
+ }
+ Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
+ content(dragging)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
new file mode 100644
index 000000000000..e77ade91a93b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.fold.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.window.layout.WindowInfoTracker
+import com.android.systemui.fold.ui.helper.FoldPosture
+import com.android.systemui.fold.ui.helper.foldPostureInternal
+
+/** Returns the [FoldPosture] of the device currently. */
+@Composable
+fun foldPosture(): State<FoldPosture> {
+ val context = LocalContext.current
+ val infoTracker = remember(context) { WindowInfoTracker.getOrCreate(context) }
+ val layoutInfo by infoTracker.windowLayoutInfo(context).collectAsState(initial = null)
+
+ return produceState<FoldPosture>(
+ initialValue = FoldPosture.Folded,
+ key1 = layoutInfo,
+ ) {
+ value = foldPostureInternal(layoutInfo)
+ }
+}
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 ee310ab41373..cc95a4b72731 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
@@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.toComposeRect
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
@@ -41,6 +42,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.notifications.ui.composable.NotificationStack
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
@@ -88,7 +90,7 @@ constructor(
) {
LockscreenScene(
viewProvider = viewProvider,
- longPressViewModel = viewModel.longPress,
+ viewModel = viewModel,
modifier = modifier,
)
}
@@ -108,9 +110,9 @@ constructor(
}
@Composable
-private fun LockscreenScene(
+private fun SceneScope.LockscreenScene(
viewProvider: () -> View,
- longPressViewModel: KeyguardLongPressViewModel,
+ viewModel: LockscreenSceneViewModel,
modifier: Modifier = Modifier,
) {
fun findSettingsMenu(): View {
@@ -121,7 +123,7 @@ private fun LockscreenScene(
modifier = modifier,
) {
LongPressSurface(
- viewModel = longPressViewModel,
+ viewModel = viewModel.longPress,
isSettingsMenuVisible = { findSettingsMenu().isVisible },
settingsMenuBounds = {
val bounds = android.graphics.Rect()
@@ -141,6 +143,28 @@ private fun LockscreenScene(
},
modifier = Modifier.fillMaxSize(),
)
+
+ val notificationStackPosition by
+ viewModel.keyguardRoot.notificationPositionOnLockscreen.collectAsState()
+
+ Layout(
+ modifier = Modifier.fillMaxSize(),
+ content = {
+ NotificationStack(
+ viewModel = viewModel.notifications,
+ isScrimVisible = false,
+ )
+ }
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val height = notificationStackPosition.height.toInt()
+ val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
+ val placeable = measurables[0].measure(childConstraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ val start = (constraints.maxWidth - placeable.measuredWidth) / 2
+ placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
+ }
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index dd71dfa0b008..c9d31fdcb8e5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -17,56 +17,197 @@
package com.android.systemui.notifications.ui.composable
+import android.util.Log
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
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.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.positionInWindow
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.animateSharedFloatAsState
+import com.android.systemui.notifications.ui.composable.Notifications.Form
+import com.android.systemui.notifications.ui.composable.Notifications.SharedValues.SharedExpansionValue
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
object Notifications {
object Elements {
- val Notifications = ElementKey("Notifications")
+ val NotificationScrim = ElementKey("NotificationScrim")
+ val NotificationPlaceholder = ElementKey("NotificationPlaceholder")
+ val ShelfSpace = ElementKey("ShelfSpace")
+ }
+
+ object SharedValues {
+ val SharedExpansionValue = ValueKey("SharedExpansionValue")
+ }
+
+ enum class Form {
+ HunFromTop,
+ Stack,
+ HunFromBottom,
}
}
+/**
+ * Adds the space where heads up notifications can appear in the scene. This should generally be the
+ * entire size of the scene.
+ */
@Composable
-fun SceneScope.Notifications(
+fun SceneScope.HeadsUpNotificationSpace(
+ viewModel: NotificationsPlaceholderViewModel,
+ isPeekFromBottom: Boolean = false,
modifier: Modifier = Modifier,
) {
- // TODO(b/272779828): implement.
- Column(
+ NotificationPlaceholder(
+ viewModel = viewModel,
+ form = if (isPeekFromBottom) Form.HunFromBottom else Form.HunFromTop,
+ modifier = modifier,
+ )
+}
+
+/** Adds the space where notification stack will appear in the scene. */
+@Composable
+fun SceneScope.NotificationStack(
+ viewModel: NotificationsPlaceholderViewModel,
+ isScrimVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Box(modifier = modifier) {
+ if (isScrimVisible) {
+ Box(
+ modifier =
+ Modifier.element(Notifications.Elements.NotificationScrim)
+ .fillMaxSize()
+ .clip(RoundedCornerShape(32.dp))
+ .background(MaterialTheme.colorScheme.surface)
+ )
+ }
+ NotificationPlaceholder(
+ viewModel = viewModel,
+ form = Form.Stack,
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
+}
+
+/**
+ * This may be added to the lockscreen to provide a space to the start of the lock icon where the
+ * short shelf has room to flow vertically below the lock icon, but to its start, allowing more
+ * notifications to fit in the stack itself. (see: b/213934746)
+ *
+ * NOTE: this is totally unused for now; it is here to clarify the future plan
+ */
+@Composable
+fun SceneScope.NotificationShelfSpace(
+ viewModel: NotificationsPlaceholderViewModel,
+ modifier: Modifier = Modifier,
+) {
+ Text(
+ text = "Shelf Space",
+ modifier
+ .element(key = Notifications.Elements.ShelfSpace)
+ .fillMaxWidth()
+ .onSizeChanged { size: IntSize ->
+ debugLog(viewModel) { "SHELF onSizeChanged: size=$size" }
+ }
+ .onPlaced { coordinates: LayoutCoordinates ->
+ debugLog(viewModel) {
+ ("SHELF onPlaced:" +
+ " size=${coordinates.size}" +
+ " position=${coordinates.positionInWindow()}" +
+ " bounds=${coordinates.boundsInWindow()}")
+ }
+ }
+ .clip(RoundedCornerShape(24.dp))
+ .background(MaterialTheme.colorScheme.primaryContainer)
+ .padding(16.dp),
+ style = MaterialTheme.typography.titleLarge,
+ color = MaterialTheme.colorScheme.onPrimaryContainer,
+ )
+}
+
+@Composable
+private fun SceneScope.NotificationPlaceholder(
+ viewModel: NotificationsPlaceholderViewModel,
+ form: Form,
+ modifier: Modifier = Modifier,
+) {
+ val key = Notifications.Elements.NotificationPlaceholder
+ Box(
modifier =
modifier
- .element(key = Notifications.Elements.Notifications)
- .fillMaxWidth()
- .defaultMinSize(minHeight = 300.dp)
- .clip(RoundedCornerShape(32.dp))
- .background(MaterialTheme.colorScheme.surface)
- .padding(16.dp),
+ .element(key)
+ .debugBackground(viewModel)
+ .onSizeChanged { size: IntSize ->
+ debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
+ }
+ .onPlaced { coordinates: LayoutCoordinates ->
+ debugLog(viewModel) {
+ "STACK onPlaced:" +
+ " size=${coordinates.size}" +
+ " position=${coordinates.positionInWindow()}" +
+ " bounds=${coordinates.boundsInWindow()}"
+ }
+ val boundsInWindow = coordinates.boundsInWindow()
+ viewModel.setPlaceholderPositionInWindow(
+ top = boundsInWindow.top,
+ bottom = boundsInWindow.bottom,
+ )
+ }
) {
- Text(
- text = "Notifications",
- modifier = Modifier.align(Alignment.CenterHorizontally),
- style = MaterialTheme.typography.titleLarge,
- color = MaterialTheme.colorScheme.onSurface,
- )
- Spacer(modifier = Modifier.weight(1f))
- Text(
- text = "Shelf",
- modifier = Modifier.align(Alignment.CenterHorizontally),
- style = MaterialTheme.typography.titleSmall,
- color = MaterialTheme.colorScheme.onSurface,
- )
+ val animatedExpansion by
+ animateSharedFloatAsState(
+ value = if (form == Form.HunFromTop) 0f else 1f,
+ key = SharedExpansionValue,
+ element = key
+ )
+ debugLog(viewModel) { "STACK composed: expansion=$animatedExpansion" }
+ if (viewModel.isPlaceholderTextVisible) {
+ Text(
+ text = "Notifications",
+ style = MaterialTheme.typography.titleLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+}
+
+private inline fun debugLog(
+ viewModel: NotificationsPlaceholderViewModel,
+ msg: () -> Any,
+) {
+ if (viewModel.isDebugLoggingEnabled) {
+ Log.d(TAG, msg().toString())
}
}
+
+private fun Modifier.debugBackground(
+ viewModel: NotificationsPlaceholderViewModel,
+ color: Color = DEBUG_COLOR,
+): Modifier =
+ if (viewModel.isVisualDebuggingEnabled) {
+ background(color)
+ } else {
+ this
+ }
+
+private const val TAG = "FlexiNotifs"
+private val DEBUG_COLOR = Color(1f, 0f, 0f, 0.2f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt
deleted file mode 100644
index c84a5e91ca50..000000000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt
+++ /dev/null
@@ -1,79 +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.qs.footer.ui.compose
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.layout.fillMaxWidth
-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.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneScope
-
-object QuickSettings {
- object Elements {
- // TODO RENAME
- val Content = ElementKey("QuickSettingsContent")
- val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
- val FooterActions = ElementKey("QuickSettingsFooterActions")
- }
-}
-
-@Composable
-fun SceneScope.QuickSettings(
- modifier: Modifier = Modifier,
-) {
- // TODO(b/272780058): implement.
- Column(
- modifier =
- modifier
- .element(QuickSettings.Elements.Content)
- .fillMaxWidth()
- .defaultMinSize(minHeight = 300.dp)
- .clip(RoundedCornerShape(32.dp))
- .background(MaterialTheme.colorScheme.primary)
- .padding(16.dp),
- ) {
- Text(
- text = "Quick settings grid",
- modifier =
- Modifier.element(QuickSettings.Elements.CollapsedGrid)
- .align(Alignment.CenterHorizontally),
- style = MaterialTheme.typography.titleLarge,
- color = MaterialTheme.colorScheme.onPrimary,
- )
- Spacer(modifier = Modifier.weight(1f))
- Text(
- text = "QS footer actions",
- modifier =
- Modifier.element(QuickSettings.Elements.FooterActions)
- .align(Alignment.CenterHorizontally),
- style = MaterialTheme.typography.titleSmall,
- color = MaterialTheme.colorScheme.onPrimary,
- )
- }
-}
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
new file mode 100644
index 000000000000..765468372604
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.ui.composable
+
+import android.view.ContextThemeWrapper
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+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.SceneScope
+import com.android.compose.theme.colorAttr
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.res.R
+
+object QuickSettings {
+ object Elements {
+ // TODO RENAME
+ val Content = ElementKey("QuickSettingsContent")
+ val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
+ val FooterActions = ElementKey("QuickSettingsFooterActions")
+ }
+}
+
+@Composable
+private fun QuickSettingsTheme(content: @Composable () -> Unit) {
+ val context = LocalContext.current
+ val themedContext =
+ remember(context) { ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) }
+ CompositionLocalProvider(LocalContext provides themedContext) { content() }
+}
+
+@Composable
+fun SceneScope.QuickSettings(
+ modifier: Modifier = Modifier,
+ qsSceneAdapter: QSSceneAdapter,
+ state: QSSceneAdapter.State
+) {
+ // TODO(b/272780058): implement.
+ Column(
+ modifier =
+ modifier
+ .element(QuickSettings.Elements.Content)
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = 300.dp)
+ ) {
+ QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, state)
+ }
+}
+
+@Composable
+private fun QuickSettingsContent(
+ qsSceneAdapter: QSSceneAdapter,
+ state: QSSceneAdapter.State,
+ modifier: Modifier = Modifier,
+) {
+ val qsView by qsSceneAdapter.qsView.collectAsState(null)
+ QuickSettingsTheme {
+ val context = LocalContext.current
+
+ val frame by remember(context) { mutableStateOf(FrameLayout(context)) }
+
+ LaunchedEffect(key1 = context) {
+ if (qsView == null) {
+ qsSceneAdapter.inflate(context, frame)
+ }
+ }
+ qsView?.let {
+ it.attachToParent(frame)
+ AndroidView(
+ modifier = modifier.fillMaxSize().background(colorAttr(R.attr.underSurface)),
+ factory = { _ ->
+ qsSceneAdapter.setState(state)
+ frame
+ },
+ onRelease = { frame.removeAllViews() },
+ update = { qsSceneAdapter.setState(state) }
+ )
+ }
+ }
+}
+
+private fun View.attachToParent(parent: ViewGroup) {
+ if (this.parent != null && this.parent != parent) {
+ (this.parent as ViewGroup).removeView(this)
+ }
+ if (this.parent != parent) {
+ parent.addView(
+ this,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ )
+ }
+}
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 a33eac55ac3e..871d9f9b7627 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
@@ -17,43 +17,57 @@
package com.android.systemui.qs.ui.composable
import android.view.ViewGroup
-import androidx.compose.foundation.clickable
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
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.platform.LocalDensity
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.footer.ui.compose.QuickSettings
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
-import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
-import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
import com.android.systemui.statusbar.phone.StatusBarLocation
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
/** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */
@SysUISingleton
class QuickSettingsScene
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
private val viewModel: QuickSettingsSceneViewModel,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
@@ -61,14 +75,12 @@ constructor(
) : ComposableScene {
override val key = SceneKey.QuickSettings
- private val _destinationScenes =
- MutableStateFlow<Map<UserAction, SceneModel>>(
- mapOf(
- UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
- )
- )
- .asStateFlow()
- override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = _destinationScenes
+ override val destinationScenes =
+ viewModel.destinationScenes.stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = emptyMap(),
+ )
@Composable
override fun SceneScope.Content(
@@ -93,31 +105,65 @@ private fun SceneScope.QuickSettingsScene(
modifier: Modifier = Modifier,
) {
// TODO(b/280887232): implement the real UI.
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier =
- modifier
- .fillMaxSize()
- .clickable(onClick = { viewModel.onContentClicked() })
- .padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
- ) {
- when (LocalWindowSizeClass.current.widthSizeClass) {
- WindowWidthSizeClass.Compact ->
- ExpandedShadeHeader(
- viewModel = viewModel.shadeHeaderViewModel,
- createTintedIconManager = createTintedIconManager,
- createBatteryMeterViewController = createBatteryMeterViewController,
- statusBarIconController = statusBarIconController,
- )
- else ->
- CollapsedShadeHeader(
- viewModel = viewModel.shadeHeaderViewModel,
- createTintedIconManager = createTintedIconManager,
- createBatteryMeterViewController = createBatteryMeterViewController,
- statusBarIconController = statusBarIconController,
+ Box(modifier = modifier.fillMaxSize()) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
+ val collapsedHeaderHeight =
+ with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
+ Spacer(
+ modifier =
+ Modifier.element(Shade.Elements.ScrimBackground)
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim)
+ )
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier =
+ Modifier.fillMaxSize().padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
+ ) {
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Compact ->
+ AnimatedVisibility(
+ visible = !isCustomizing,
+ enter =
+ expandVertically(
+ animationSpec = tween(1000),
+ initialHeight = { collapsedHeaderHeight },
+ ) + fadeIn(tween(1000)),
+ exit =
+ shrinkVertically(
+ animationSpec = tween(1000),
+ targetHeight = { collapsedHeaderHeight },
+ shrinkTowards = Alignment.Top,
+ ) + fadeOut(tween(1000)),
+ ) {
+ ExpandedShadeHeader(
+ viewModel = viewModel.shadeHeaderViewModel,
+ createTintedIconManager = createTintedIconManager,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ statusBarIconController = statusBarIconController,
+ )
+ }
+ else ->
+ CollapsedShadeHeader(
+ viewModel = viewModel.shadeHeaderViewModel,
+ createTintedIconManager = createTintedIconManager,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ statusBarIconController = statusBarIconController,
+ )
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ QuickSettings(
+ modifier = Modifier.fillMaxHeight(),
+ viewModel.qsSceneAdapter,
+ QSSceneAdapter.State.QS
)
+ }
}
- Spacer(modifier = Modifier.height(16.dp))
- QuickSettings()
+ HeadsUpNotificationSpace(
+ viewModel = viewModel.notifications,
+ isPeekFromBottom = true,
+ modifier = Modifier.padding(16.dp).fillMaxSize(),
+ )
}
}
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 f35ea8373db7..bded98d52481 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
@@ -17,15 +17,20 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
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.SceneModel
import com.android.systemui.scene.shared.model.UserAction
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -36,7 +41,11 @@ import kotlinx.coroutines.flow.asStateFlow
* content from the scene framework.
*/
@SysUISingleton
-class GoneScene @Inject constructor() : ComposableScene {
+class GoneScene
+@Inject
+constructor(
+ private val notificationsViewModel: NotificationsPlaceholderViewModel,
+) : ComposableScene {
override val key = SceneKey.Gone
override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
@@ -56,6 +65,11 @@ class GoneScene @Inject constructor() : ComposableScene {
override fun SceneScope.Content(
modifier: Modifier,
) {
- Box(modifier = modifier)
+ Box(modifier = modifier) {
+ HeadsUpNotificationSpace(
+ viewModel = notificationsViewModel,
+ modifier = Modifier.padding(16.dp).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 0da562bcb3bb..4eb9089dc589 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
@@ -18,7 +18,6 @@
package com.android.systemui.scene.ui.composable
-import android.os.SystemProperties
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
@@ -84,7 +83,6 @@ fun SceneContainer(
val currentDestinations: Map<UserAction, SceneModel> by
currentScene.destinationScenes.collectAsState()
val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
- val isRibbonEnabled = remember { SystemProperties.getBoolean("flexi.ribbon", false) }
DisposableEffect(viewModel, state) {
viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() })
@@ -137,17 +135,15 @@ fun SceneContainer(
}
}
- if (isRibbonEnabled) {
- BottomRightCornerRibbon(
- content = {
- Text(
- text = "flexi\uD83E\uDD43",
- color = Color.White,
- )
- },
- modifier = Modifier.align(Alignment.BottomEnd),
- )
- }
+ BottomRightCornerRibbon(
+ content = {
+ Text(
+ text = "flexi\uD83E\uDD43",
+ color = Color.White,
+ )
+ },
+ modifier = Modifier.align(Alignment.BottomEnd),
+ )
}
}
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 45df2b1bb20c..6bb525aa00fb 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
@@ -3,10 +3,12 @@ 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.scene.ui.composable.Shade
fun TransitionBuilder.goneToShadeTransition() {
spec = tween(durationMillis = 500)
translate(Shade.rootElementKey, Edge.Top, true)
+ fade(Notifications.Elements.NotificationScrim)
}
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 7ecfb62c4f62..ebc343dc6d76 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
@@ -4,13 +4,12 @@ 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.footer.ui.compose.QuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
fun TransitionBuilder.lockscreenToShadeTransition() {
spec = tween(durationMillis = 500)
- punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim)
translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false)
fractionRange(end = 0.5f) {
fade(Shade.Elements.ScrimBackground)
@@ -20,5 +19,5 @@ fun TransitionBuilder.lockscreenToShadeTransition() {
startsOutsideLayoutBounds = false,
)
}
- fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) }
+ fractionRange(start = 0.5f) { fade(Notifications.Elements.NotificationScrim) }
}
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 be85beea6ee0..d5c2a03b3f9f 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
@@ -4,13 +4,13 @@ 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.footer.ui.compose.QuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.ShadeHeader
fun TransitionBuilder.shadeToQuickSettingsTransition() {
spec = tween(durationMillis = 500)
- translate(Notifications.Elements.Notifications, Edge.Bottom)
+ translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
translate(ShadeHeader.Elements.CollapsedContent, y = ShadeHeader.Dimensions.CollapsedHeight)
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 13ebdf9c4a7c..2df151bc2d8e 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
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -36,8 +37,9 @@ import com.android.compose.animation.scene.SceneScope
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.notifications.ui.composable.Notifications
-import com.android.systemui.qs.footer.ui.compose.QuickSettings
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -152,9 +154,17 @@ private fun SceneScope.ShadeScene(
statusBarIconController = statusBarIconController,
)
Spacer(modifier = Modifier.height(16.dp))
- QuickSettings(modifier = Modifier.height(160.dp))
+ QuickSettings(
+ modifier = Modifier.wrapContentHeight(),
+ viewModel.qsSceneAdapter,
+ QSSceneAdapter.State.QQS
+ )
Spacer(modifier = Modifier.height(16.dp))
- Notifications(modifier = Modifier.weight(1f))
+ NotificationStack(
+ viewModel = viewModel.notifications,
+ isScrimVisible = true,
+ modifier = Modifier.weight(1f),
+ )
}
}
}
diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp
index 050d1d5651ad..3424085049cc 100644
--- a/packages/SystemUI/compose/scene/Android.bp
+++ b/packages/SystemUI/compose/scene/Android.bp
@@ -21,12 +21,19 @@ package {
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}
+filegroup {
+ name: "PlatformComposeSceneTransitionLayout-srcs",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
+
android_library {
name: "PlatformComposeSceneTransitionLayout",
manifest: "AndroidManifest.xml",
srcs: [
- "src/**/*.kt",
+ ":PlatformComposeSceneTransitionLayout-srcs",
],
static_libs: [
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 eb10afc1ee57..2b1195229c76 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
@@ -17,38 +17,38 @@
package com.android.compose.animation.scene
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.geometry.lerp
-import androidx.compose.ui.graphics.graphicsLayer
+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.Measurable
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.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.SharedElementTransformation
-import com.android.compose.modifiers.thenIf
import com.android.compose.ui.util.lerp
+import kotlinx.coroutines.launch
/** An element on screen, that can be composed in one or more scenes. */
+@Stable
internal class Element(val key: ElementKey) {
/**
* The last values of this element, coming from any scene. Note that this value will be unstable
@@ -85,21 +85,33 @@ internal class Element(val key: ElementKey) {
/** The size of this element. */
var size = SizeUnspecified
+ /** The draw scale of this element. */
+ var drawScale = Scale.Default
+
/** The alpha of this element. */
var alpha = AlphaUnspecified
}
/** The target values of this element in a given scene. */
- class TargetValues {
+ @Stable
+ class TargetValues(val scene: SceneKey) {
val lastValues = Values()
var targetSize by mutableStateOf(SizeUnspecified)
var targetOffset by mutableStateOf(Offset.Unspecified)
val sharedValues = SnapshotStateMap<ValueKey, SharedValue<*>>()
+
+ /**
+ * The attached [ElementNode] a Modifier.element() for a given element and scene. During
+ * composition, this set could have 0 to 2 elements. After composition and after all
+ * modifier nodes have been attached/detached, this set should contain exactly 1 element.
+ */
+ val nodes = mutableSetOf<ElementNode>()
}
/** A shared value of this element. */
+ @Stable
class SharedValue<T>(val key: ValueKey, initialValue: T) {
var value by mutableStateOf(initialValue)
}
@@ -110,78 +122,145 @@ internal class Element(val key: ElementKey) {
}
}
+data class Scale(val scaleX: Float, val scaleY: Float, val pivot: Offset = Offset.Unspecified) {
+
+ companion object {
+ val Default = Scale(1f, 1f, Offset.Unspecified)
+ }
+}
+
/** The implementation of [SceneScope.element]. */
@OptIn(ExperimentalComposeUiApi::class)
+@Stable
internal fun Modifier.element(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
key: ElementKey,
-): Modifier = composed {
- val sceneValues = remember(scene, key) { Element.TargetValues() }
- val element =
- // Get the element associated to [key] if it was already composed in another scene,
- // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a
- // withoutReadObservation() because there is no need to recompose when that map is mutated.
- Snapshot.withoutReadObservation {
- val element =
- layoutImpl.elements[key] ?: Element(key).also { layoutImpl.elements[key] = it }
- val previousValues = element.sceneValues[scene.key]
- if (previousValues == null) {
- element.sceneValues[scene.key] = sceneValues
- } else if (previousValues != sceneValues) {
- error("$key was composed multiple times in $scene")
- }
+): Modifier {
+ val element: Element
+ val sceneValues: Element.TargetValues
+
+ // Get the element associated to [key] if it was already composed in another scene,
+ // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a
+ // withoutReadObservation() because there is no need to recompose when that map is mutated.
+ Snapshot.withoutReadObservation {
+ element = layoutImpl.elements[key] ?: Element(key).also { layoutImpl.elements[key] = it }
+ sceneValues =
+ element.sceneValues[scene.key]
+ ?: Element.TargetValues(scene.key).also { element.sceneValues[scene.key] = it }
+ }
- element
+ return this.then(ElementModifier(layoutImpl, scene, element, sceneValues))
+ // TODO(b/311132415): Move this into ElementNode once we can create a delegate
+ // IntermediateLayoutModifierNode.
+ .intermediateLayout { measurable, constraints ->
+ val placeable =
+ measure(layoutImpl, scene, element, sceneValues, measurable, constraints)
+ layout(placeable.width, placeable.height) {
+ place(layoutImpl, scene, element, sceneValues, placeable, placementScope = this)
+ }
}
- val lastSharedValues = element.lastSharedValues
- val lastSceneValues = sceneValues.lastValues
+ .testTag(key.testTag)
+}
+
+/**
+ * An element associated to [ElementNode]. Note that this element does not support updates as its
+ * arguments should always be the same.
+ */
+private data class ElementModifier(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val scene: Scene,
+ private val element: Element,
+ private val sceneValues: Element.TargetValues,
+) : ModifierNodeElement<ElementNode>() {
+ override fun create(): ElementNode = ElementNode(layoutImpl, scene, element, sceneValues)
+
+ override fun update(node: ElementNode) {
+ node.update(layoutImpl, scene, element, sceneValues)
+ }
+}
+
+internal class ElementNode(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: Scene,
+ element: Element,
+ sceneValues: Element.TargetValues,
+) : Modifier.Node(), DrawModifierNode {
+ private var layoutImpl: SceneTransitionLayoutImpl = layoutImpl
+ private var scene: Scene = scene
+ private var element: Element = element
+ private var sceneValues: Element.TargetValues = sceneValues
+
+ override fun onAttach() {
+ super.onAttach()
+ addNodeToSceneValues()
+ }
- DisposableEffect(scene, sceneValues, element) {
- onDispose {
- element.sceneValues.remove(scene.key)
+ private fun addNodeToSceneValues() {
+ sceneValues.nodes.add(this)
- // This was the last scene this element was in, so remove it from the map.
- if (element.sceneValues.isEmpty()) {
- layoutImpl.elements.remove(element.key)
+ coroutineScope.launch {
+ // At this point all [CodeLocationNode] have been attached or detached, which means that
+ // [sceneValues.codeLocations] should have exactly 1 element, otherwise this means that
+ // this element was composed multiple times in the same scene.
+ val nCodeLocations = sceneValues.nodes.size
+ if (nCodeLocations != 1 || !sceneValues.nodes.contains(this@ElementNode)) {
+ error("${element.key} was composed $nCodeLocations times in ${sceneValues.scene}")
}
}
}
- val alpha =
- remember(layoutImpl, element, scene, sceneValues) {
- derivedStateOf { elementAlpha(layoutImpl, element, scene, sceneValues) }
+ override fun onDetach() {
+ super.onDetach()
+ removeNodeFromSceneValues()
+ }
+
+ private fun removeNodeFromSceneValues() {
+ sceneValues.nodes.remove(this)
+
+ // If element is not composed from this scene anymore, remove the scene values. This works
+ // because [onAttach] is called before [onDetach], so if an element is moved from the UI
+ // tree we will first add the new code location then remove the old one.
+ if (sceneValues.nodes.isEmpty()) {
+ element.sceneValues.remove(sceneValues.scene)
}
- val isOpaque by remember(alpha) { derivedStateOf { alpha.value == 1f } }
- SideEffect {
- if (isOpaque) {
- lastSharedValues.alpha = 1f
- lastSceneValues.alpha = 1f
+
+ // If the element is not composed in any scene, remove it from the elements map.
+ if (element.sceneValues.isEmpty()) {
+ layoutImpl.elements.remove(element.key)
}
}
- drawWithContent {
- if (shouldDrawElement(layoutImpl, scene, element)) {
+ fun update(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: Scene,
+ element: Element,
+ sceneValues: Element.TargetValues,
+ ) {
+ removeNodeFromSceneValues()
+ this.layoutImpl = layoutImpl
+ this.scene = scene
+ this.element = element
+ this.sceneValues = sceneValues
+ addNodeToSceneValues()
+ }
+
+ override fun ContentDrawScope.draw() {
+ if (shouldDrawElement(layoutImpl, scene, element)) {
+ val drawScale = getDrawScale(layoutImpl, element, scene, sceneValues)
+ if (drawScale == Scale.Default) {
drawContent()
+ } else {
+ scale(
+ drawScale.scaleX,
+ drawScale.scaleY,
+ if (drawScale.pivot.isUnspecified) center else drawScale.pivot,
+ ) {
+ this@draw.drawContent()
+ }
}
}
- .modifierTransformations(layoutImpl, scene, element, sceneValues)
- .intermediateLayout { measurable, constraints ->
- val placeable =
- measure(layoutImpl, scene, element, sceneValues, measurable, constraints)
- layout(placeable.width, placeable.height) {
- place(layoutImpl, scene, element, sceneValues, placeable, placementScope = this)
- }
- }
- .thenIf(!isOpaque) {
- Modifier.graphicsLayer {
- val alpha = alpha.value
- this.alpha = alpha
- lastSharedValues.alpha = alpha
- lastSceneValues.alpha = alpha
- }
- }
- .testTag(key.testTag)
+ }
}
private fun shouldDrawElement(
@@ -266,38 +345,60 @@ internal fun sharedElementTransformation(
}
/**
- * Chain the [com.android.compose.animation.scene.transformation.ModifierTransformation] applied
- * throughout the current transition, if any.
+ * Whether the element is opaque or not.
+ *
+ * Important: The logic here should closely match the logic in [elementAlpha]. Note that we don't
+ * reuse [elementAlpha] and simply check if alpha == 1f because [isElementOpaque] is checked during
+ * placement and we don't want to read the transition progress in that phase.
*/
-private fun Modifier.modifierTransformations(
+private fun isElementOpaque(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
element: Element,
+ scene: Scene,
sceneValues: Element.TargetValues,
-): Modifier {
- when (val state = layoutImpl.state.transitionState) {
- is TransitionState.Idle -> return this
- is TransitionState.Transition -> {
- val fromScene = state.fromScene
- val toScene = state.toScene
- if (fromScene == toScene) {
- // Same as idle.
- return this
- }
+): Boolean {
+ val state = layoutImpl.state.transitionState
- return layoutImpl.transitions
- .transitionSpec(fromScene, state.toScene)
- .transformations(element.key, scene.key)
- .modifier
- .fold(this) { modifier, transformation ->
- with(transformation) {
- modifier.transform(layoutImpl, scene, element, sceneValues)
- }
- }
- }
+ if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+ return true
+ }
+
+ if (!layoutImpl.isTransitionReady(state)) {
+ val lastValue =
+ sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
+ ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f
+
+ return lastValue == 1f
+ }
+
+ val fromScene = state.fromScene
+ val toScene = state.toScene
+ val fromValues = element.sceneValues[fromScene]
+ val toValues = element.sceneValues[toScene]
+
+ if (fromValues == null && toValues == null) {
+ error("This should not happen, element $element is neither in $fromScene or $toScene")
+ }
+
+ val isSharedElement = fromValues != null && toValues != null
+ if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
+ return true
}
+
+ return layoutImpl.transitions
+ .transitionSpec(fromScene, toScene)
+ .transformations(element.key, scene.key)
+ .alpha == null
}
+/**
+ * Whether the element is opaque or not.
+ *
+ * Important: The logic here should closely match the logic in [isElementOpaque]. Note that we don't
+ * reuse [elementAlpha] in [isElementOpaque] and simply check if alpha == 1f because
+ * [isElementOpaque] is checked during placement and we don't want to read the transition progress
+ * in that phase.
+ */
private fun elementAlpha(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
@@ -377,6 +478,28 @@ private fun IntermediateMeasureScope.measure(
return placeable
}
+private fun getDrawScale(
+ layoutImpl: SceneTransitionLayoutImpl,
+ element: Element,
+ scene: Scene,
+ sceneValues: Element.TargetValues
+): Scale {
+ return computeValue(
+ layoutImpl,
+ scene,
+ element,
+ sceneValue = { Scale.Default },
+ transformation = { it.drawScale },
+ idleValue = Scale.Default,
+ currentValue = { Scale.Default },
+ lastValue = {
+ sceneValues.lastValues.drawScale.takeIf { it != Scale.Default }
+ ?: element.lastSharedValues.drawScale
+ },
+ ::lerp,
+ )
+}
+
@OptIn(ExperimentalComposeUiApi::class)
private fun IntermediateMeasureScope.place(
layoutImpl: SceneTransitionLayoutImpl,
@@ -397,6 +520,8 @@ private fun IntermediateMeasureScope.place(
}
val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
+ val lastSharedValues = element.lastSharedValues
+ val lastValues = sceneValues.lastValues
val targetOffset =
computeValue(
layoutImpl,
@@ -407,16 +532,31 @@ private fun IntermediateMeasureScope.place(
idleValue = targetOffsetInScene,
currentValue = { currentOffset },
lastValue = {
- sceneValues.lastValues.offset.takeIf { it.isSpecified }
- ?: element.lastSharedValues.offset.takeIf { it.isSpecified }
- ?: currentOffset
+ lastValues.offset.takeIf { it.isSpecified }
+ ?: lastSharedValues.offset.takeIf { it.isSpecified } ?: currentOffset
},
::lerp,
)
- element.lastSharedValues.offset = targetOffset
- sceneValues.lastValues.offset = targetOffset
- placeable.place((targetOffset - currentOffset).round())
+ lastSharedValues.offset = targetOffset
+ lastValues.offset = targetOffset
+
+ val offset = (targetOffset - currentOffset).round()
+ if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
+ // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
+ // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
+ // case.
+ placeable.place(offset)
+ lastSharedValues.alpha = 1f
+ lastValues.alpha = 1f
+ } else {
+ placeable.placeWithLayer(offset) {
+ val alpha = elementAlpha(layoutImpl, element, scene, sceneValues)
+ this.alpha = alpha
+ lastSharedValues.alpha = alpha
+ lastValues.alpha = alpha
+ }
+ }
}
}
@@ -457,7 +597,10 @@ private inline fun <T> computeValue(
// There is no ongoing transition.
if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
- return idleValue
+ // Even if this element SceneTransitionLayout is not animated, the layout itself might be
+ // animated (e.g. by another parent SceneTransitionLayout), in which case this element still
+ // need to participate in the layout phase.
+ return currentValue()
}
// A transition was started but it's not ready yet (not all elements have been composed/laid
@@ -475,21 +618,17 @@ private inline fun <T> computeValue(
error("This should not happen, element $element is neither in $fromScene or $toScene")
}
- // TODO(b/291053278): Handle overscroll correctly. We should probably coerce between [0f, 1f]
- // here and consume overflows at drawing time, somehow reusing Compose OverflowEffect or some
- // similar mechanism.
- val transitionProgress = state.progress
-
// The element is shared: interpolate between the value in fromScene and the value in toScene.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
val isSharedElement = fromValues != null && toValues != null
if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
- return lerp(
- sceneValue(fromValues!!),
- sceneValue(toValues!!),
- transitionProgress,
- )
+ val start = sceneValue(fromValues!!)
+ val end = sceneValue(toValues!!)
+
+ // Make sure we don't read progress if values are the same and we don't need to interpolate,
+ // so we don't invalidate the phase where this is read.
+ return if (start == end) start else lerp(start, end, state.progress)
}
val transformation =
@@ -524,8 +663,15 @@ private inline fun <T> computeValue(
idleValue,
)
+ // Make sure we don't read progress if values are the same and we don't need to interpolate, so
+ // we don't invalidate the phase where this is read.
+ if (targetValue == idleValue) {
+ return targetValue
+ }
+
+ val progress = state.progress
// TODO(b/290184746): Make sure that we don't overflow transformations associated to a range.
- val rangeProgress = transformation.range?.progress(transitionProgress) ?: transitionProgress
+ val rangeProgress = transformation.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the value before entering/after leaving.
val isEntering = scene.key == toScene
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
index 216608aff0cb..5d8eaf7f3d15 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
@@ -2,9 +2,10 @@ package com.android.compose.animation.scene
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.unit.IntSize
interface DraggableHandler {
- fun onDragStarted(startedPosition: Offset, pointersDown: Int = 1)
+ fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int = 1)
fun onDelta(pixels: Float)
fun onDragStopped(velocity: Float)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index bc015eedb1b4..84d3b8647d6c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -17,11 +17,13 @@
package com.android.compose.animation.scene
import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.Stable
/**
* A base class to create unique keys, associated to an [identity] that is used to check the
* equality of two key instances.
*/
+@Stable
sealed class Key(val debugName: String, val identity: Any) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -43,7 +45,10 @@ class SceneKey(
name: String,
identity: Any = Object(),
) : Key(name, identity) {
- @VisibleForTesting val testTag: String = "scene:$name"
+ @VisibleForTesting
+ // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
+ // access internal members.
+ val testTag: String = "scene:$name"
/** The unique [ElementKey] identifying this scene's root element. */
val rootElementKey = ElementKey(name, identity)
@@ -64,7 +69,10 @@ class ElementKey(
*/
val isBackground: Boolean = false,
) : Key(name, identity), ElementMatcher {
- @VisibleForTesting val testTag: String = "element:$name"
+ @VisibleForTesting
+ // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
+ // access internal members.
+ val testTag: String = "element:$name"
override fun matches(key: ElementKey, scene: SceneKey): Boolean {
return key == this
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index d0a5f5bfebc0..a0fba8076517 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -23,21 +23,27 @@ import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellati
import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.gestures.verticalDrag
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastForEach
@@ -55,40 +61,112 @@ import androidx.compose.ui.util.fastForEach
* dragged) and a second pointer is down and dragged. This is an implementation detail that might
* change in the future.
*/
-// TODO(b/291055080): Migrate to the Modifier.Node API.
+@Stable
internal fun Modifier.multiPointerDraggable(
orientation: Orientation,
enabled: Boolean,
startDragImmediately: Boolean,
- onDragStarted: (startedPosition: Offset, pointersDown: Int) -> Unit,
+ onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
onDragDelta: (Float) -> Unit,
onDragStopped: (velocity: Float) -> Unit,
-): Modifier = composed {
- val onDragStarted by rememberUpdatedState(onDragStarted)
- val onDragStopped by rememberUpdatedState(onDragStopped)
- val onDragDelta by rememberUpdatedState(onDragDelta)
- val startDragImmediately by rememberUpdatedState(startDragImmediately)
-
- val velocityTracker = remember { VelocityTracker() }
- val maxFlingVelocity =
- LocalViewConfiguration.current.maximumFlingVelocity.let { max ->
- val maxF = max.toFloat()
- Velocity(maxF, maxF)
+): Modifier =
+ this.then(
+ MultiPointerDraggableElement(
+ orientation,
+ enabled,
+ startDragImmediately,
+ onDragStarted,
+ onDragDelta,
+ onDragStopped,
+ )
+ )
+
+private data class MultiPointerDraggableElement(
+ private val orientation: Orientation,
+ private val enabled: Boolean,
+ private val startDragImmediately: Boolean,
+ private val onDragStarted:
+ (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
+ private val onDragDelta: (Float) -> Unit,
+ private val onDragStopped: (velocity: Float) -> Unit,
+) : ModifierNodeElement<MultiPointerDraggableNode>() {
+ override fun create(): MultiPointerDraggableNode =
+ MultiPointerDraggableNode(
+ orientation = orientation,
+ enabled = enabled,
+ startDragImmediately = startDragImmediately,
+ onDragStarted = onDragStarted,
+ onDragDelta = onDragDelta,
+ onDragStopped = onDragStopped,
+ )
+
+ override fun update(node: MultiPointerDraggableNode) {
+ node.orientation = orientation
+ node.enabled = enabled
+ node.startDragImmediately = startDragImmediately
+ node.onDragStarted = onDragStarted
+ node.onDragDelta = onDragDelta
+ node.onDragStopped = onDragStopped
+ }
+}
+
+private class MultiPointerDraggableNode(
+ orientation: Orientation,
+ enabled: Boolean,
+ var startDragImmediately: Boolean,
+ var onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
+ var onDragDelta: (Float) -> Unit,
+ var onDragStopped: (velocity: Float) -> Unit,
+) : PointerInputModifierNode, DelegatingNode(), CompositionLocalConsumerModifierNode {
+ private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
+ private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
+ private val velocityTracker = VelocityTracker()
+
+ var enabled: Boolean = enabled
+ set(value) {
+ // Reset the pointer input whenever enabled changed.
+ if (value != field) {
+ field = value
+ delegate.resetPointerInputHandler()
+ }
+ }
+
+ var orientation: Orientation = orientation
+ set(value) {
+ // Reset the pointer input whenever enabled orientation.
+ if (value != field) {
+ field = value
+ delegate.resetPointerInputHandler()
+ }
}
- pointerInput(enabled, orientation, maxFlingVelocity) {
+ override fun onCancelPointerInput() = delegate.onCancelPointerInput()
+
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize
+ ) = delegate.onPointerEvent(pointerEvent, pass, bounds)
+
+ private suspend fun PointerInputScope.pointerInput() {
if (!enabled) {
- return@pointerInput
+ return
}
val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown ->
velocityTracker.resetTracking()
- onDragStarted(startedPosition, pointersDown)
+ onDragStarted(size, startedPosition, pointersDown)
}
val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) }
val onDragEnd: () -> Unit = {
+ val maxFlingVelocity =
+ currentValueOf(LocalViewConfiguration).maximumFlingVelocity.let { max ->
+ val maxF = max.toFloat()
+ Velocity(maxF, maxF)
+ }
+
val velocity = velocityTracker.calculateVelocity(maxFlingVelocity)
onDragStopped(
when (orientation) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
new file mode 100644
index 000000000000..560e92becba5
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Paint
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.withSaveLayer
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.toSize
+
+internal fun Modifier.punchHole(
+ layoutImpl: SceneTransitionLayoutImpl,
+ element: ElementKey,
+ bounds: ElementKey,
+ shape: Shape,
+): Modifier = this.then(PunchHoleElement(layoutImpl, element, bounds, shape))
+
+private data class PunchHoleElement(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val element: ElementKey,
+ private val bounds: ElementKey,
+ private val shape: Shape,
+) : ModifierNodeElement<PunchHoleNode>() {
+ override fun create(): PunchHoleNode = PunchHoleNode(layoutImpl, element, bounds, shape)
+
+ override fun update(node: PunchHoleNode) {
+ node.layoutImpl = layoutImpl
+ node.element = element
+ node.bounds = bounds
+ node.shape = shape
+ }
+}
+
+private class PunchHoleNode(
+ var layoutImpl: SceneTransitionLayoutImpl,
+ var element: ElementKey,
+ var bounds: ElementKey,
+ var shape: Shape,
+) : Modifier.Node(), DrawModifierNode {
+ private var lastSize: Size = Size.Unspecified
+ private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr
+ private var lastOutline: Outline? = null
+
+ override fun ContentDrawScope.draw() {
+ val bounds = layoutImpl.elements[bounds]
+
+ if (
+ bounds == null ||
+ bounds.lastSharedValues.size == Element.SizeUnspecified ||
+ bounds.lastSharedValues.offset == Offset.Unspecified
+ ) {
+ drawContent()
+ return
+ }
+
+ val element = layoutImpl.elements.getValue(element)
+ drawIntoCanvas { canvas ->
+ canvas.withSaveLayer(size.toRect(), Paint()) {
+ drawContent()
+
+ val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset
+ translate(offset.x, offset.y) { drawHole(bounds) }
+ }
+ }
+ }
+
+ private fun DrawScope.drawHole(bounds: Element) {
+ val boundsSize = bounds.lastSharedValues.size.toSize()
+ if (shape == RectangleShape) {
+ drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut)
+ return
+ }
+
+ val outline =
+ if (boundsSize == lastSize && layoutDirection == lastLayoutDirection) {
+ lastOutline!!
+ } else {
+ val newOutline = shape.createOutline(boundsSize, layoutDirection, this)
+ lastSize = boundsSize
+ lastLayoutDirection = layoutDirection
+ lastOutline = newOutline
+ newOutline
+ }
+
+ drawOutline(
+ outline,
+ Color.Black,
+ blendMode = BlendMode.DstOut,
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index eb5168bdd3cb..f5561cb404b6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -19,19 +19,24 @@ package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
/** A scene in a [SceneTransitionLayout]. */
+@Stable
internal class Scene(
val key: SceneKey,
layoutImpl: SceneTransitionLayoutImpl,
@@ -44,14 +49,24 @@ internal class Scene(
var content by mutableStateOf(content)
var userActions by mutableStateOf(actions)
var zIndex by mutableFloatStateOf(zIndex)
- var size by mutableStateOf(IntSize.Zero)
+ var targetSize by mutableStateOf(IntSize.Zero)
/** The shared values in this scene that are not tied to a specific element. */
val sharedValues = SnapshotStateMap<ValueKey, Element.SharedValue<*>>()
@Composable
+ @OptIn(ExperimentalComposeUiApi::class)
fun Content(modifier: Modifier = Modifier) {
- Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) {
+ Box(
+ modifier
+ .zIndex(zIndex)
+ .intermediateLayout { measurable, constraints ->
+ targetSize = lookaheadSize
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) { placeable.place(0, 0) }
+ }
+ .testTag(key.testTag)
+ ) {
scope.content()
}
}
@@ -65,6 +80,8 @@ private class SceneScopeImpl(
private val layoutImpl: SceneTransitionLayoutImpl,
private val scene: Scene,
) : SceneScope {
+ override val layoutState: SceneTransitionLayoutState = layoutImpl.state
+
override fun Modifier.element(key: ElementKey): Modifier {
return element(layoutImpl, scene, key)
}
@@ -91,11 +108,13 @@ private class SceneScopeImpl(
): State<T> {
val element =
element?.let { key ->
- layoutImpl.elements[key]
- ?: error(
- "Element $key is not composed. Make sure to call animateSharedXAsState " +
- "*after* Modifier.element(key)."
- )
+ Snapshot.withoutReadObservation {
+ layoutImpl.elements[key]
+ ?: error(
+ "Element $key is not composed. Make sure to call " +
+ "animateSharedXAsState *after* Modifier.element(key)."
+ )
+ }
}
return animateSharedValueAsState(
@@ -117,4 +136,10 @@ private class SceneScopeImpl(
) {
MovableElement(layoutImpl, scene, key, modifier, content)
}
+
+ override fun Modifier.punchHole(
+ element: ElementKey,
+ bounds: ElementKey,
+ shape: Shape
+ ): Modifier = punchHole(layoutImpl, element, bounds, shape)
}
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 9a3a0aef30cb..b00c88612269 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
@@ -17,7 +17,6 @@
package com.android.compose.animation.scene
import android.util.Log
-import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
@@ -27,7 +26,7 @@ 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.Velocity
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
@@ -36,15 +35,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-@VisibleForTesting
-class SceneGestureHandler(
- private val layoutImpl: SceneTransitionLayoutImpl,
+internal class SceneGestureHandler(
+ internal val layoutImpl: SceneTransitionLayoutImpl,
internal val orientation: Orientation,
private val coroutineScope: CoroutineScope,
) {
val draggable: DraggableHandler = SceneDraggableHandler(this)
- private var transitionState
+ internal var transitionState
get() = layoutImpl.state.transitionState
set(value) {
layoutImpl.state.transitionState = value
@@ -57,17 +55,15 @@ class SceneGestureHandler(
* Note: the initialScene here does not matter, it's only used for initializing the transition
* and will be replaced when a drag event starts.
*/
- private val swipeTransition = SwipeTransition(initialScene = currentScene)
+ internal val swipeTransition = SwipeTransition(initialScene = currentScene)
internal val currentScene: Scene
get() = layoutImpl.scene(transitionState.currentScene)
- @VisibleForTesting
- val isDrivingTransition
+ internal val isDrivingTransition
get() = transitionState == swipeTransition
- @VisibleForTesting
- var isAnimatingOffset
+ internal var isAnimatingOffset
get() = swipeTransition.isAnimatingOffset
private set(value) {
swipeTransition.isAnimatingOffset = value
@@ -80,7 +76,7 @@ class SceneGestureHandler(
* The velocity threshold at which the intent of the user is to swipe up or down. It is the same
* as SwipeableV2Defaults.VelocityThreshold.
*/
- @VisibleForTesting val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() }
+ internal val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() }
/**
* The positional threshold at which the intent of the user is to swipe to the next scene. It is
@@ -90,7 +86,7 @@ class SceneGestureHandler(
internal var gestureWithPriority: Any? = null
- internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) {
+ internal fun onDragStarted(pointersDown: Int, layoutSize: IntSize, startedPosition: Offset?) {
if (isDrivingTransition) {
// This [transition] was already driving the animation: simply take over it.
// Stop animating and start from where the current offset.
@@ -126,14 +122,14 @@ class SceneGestureHandler(
// we will also have to make sure that we correctly handle overscroll.
swipeTransition.absoluteDistance =
when (orientation) {
- Orientation.Horizontal -> layoutImpl.size.width
- Orientation.Vertical -> layoutImpl.size.height
+ Orientation.Horizontal -> layoutSize.width
+ Orientation.Vertical -> layoutSize.height
}.toFloat()
val fromEdge =
startedPosition?.let { position ->
layoutImpl.edgeDetector.edge(
- layoutImpl.size,
+ layoutSize,
position.round(),
layoutImpl.density,
orientation,
@@ -414,7 +410,7 @@ class SceneGestureHandler(
}
}
- private class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
+ internal class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
var _currentScene by mutableStateOf(initialScene)
override val currentScene: SceneKey
get() = _currentScene.key
@@ -513,9 +509,9 @@ class SceneGestureHandler(
private class SceneDraggableHandler(
private val gestureHandler: SceneGestureHandler,
) : DraggableHandler {
- override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
+ override fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted(pointersDown, startedPosition)
+ gestureHandler.onDragStarted(pointersDown, layoutSize, startedPosition)
}
override fun onDelta(pixels: Float) {
@@ -532,32 +528,13 @@ private class SceneDraggableHandler(
}
}
-@VisibleForTesting
-class SceneNestedScrollHandler(
+internal class SceneNestedScrollHandler(
private val gestureHandler: SceneGestureHandler,
private val startBehavior: NestedScrollBehavior,
private val endBehavior: NestedScrollBehavior,
) : NestedScrollHandler {
override val connection: PriorityNestedScrollConnection = nestedScrollConnection()
- private fun Offset.toAmount() =
- when (gestureHandler.orientation) {
- Orientation.Horizontal -> x
- Orientation.Vertical -> y
- }
-
- private fun Velocity.toAmount() =
- when (gestureHandler.orientation) {
- Orientation.Horizontal -> x
- Orientation.Vertical -> y
- }
-
- private fun Float.toOffset() =
- when (gestureHandler.orientation) {
- Orientation.Horizontal -> Offset(x = this, y = 0f)
- Orientation.Vertical -> Offset(x = 0f, y = this)
- }
-
private fun nestedScrollConnection(): PriorityNestedScrollConnection {
// If we performed a long gesture before entering priority mode, we would have to avoid
// moving on to the next scene.
@@ -595,22 +572,40 @@ class SceneNestedScrollHandler(
}
return PriorityNestedScrollConnection(
+ orientation = gestureHandler.orientation,
canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
- canChangeScene = offsetBeforeStart == Offset.Zero
- gestureHandler.isDrivingTransition &&
- canChangeScene &&
- offsetAvailable.toAmount() != 0f
+ canChangeScene = offsetBeforeStart == 0f
+
+ val canInterceptSwipeTransition =
+ canChangeScene && gestureHandler.isDrivingTransition && offsetAvailable != 0f
+ if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
+
+ val progress = gestureHandler.swipeTransition.progress
+ val threshold = gestureHandler.layoutImpl.transitionInterceptionThreshold
+ fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
+
+ // The transition is always between 0 and 1. If it is close to either of these
+ // intervals, we want to go directly to the TransitionState.Idle.
+ // The progress value can go beyond this range in the case of overscroll.
+ val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f)
+ if (shouldSnapToIdle) {
+ gestureHandler.swipeTransition.stopOffsetAnimation()
+ gestureHandler.transitionState =
+ TransitionState.Idle(gestureHandler.swipeTransition.currentScene)
+ }
+
+ // Start only if we cannot consume this event
+ !shouldSnapToIdle
},
canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
- val amount = offsetAvailable.toAmount()
val behavior: NestedScrollBehavior =
when {
- amount > 0 -> startBehavior
- amount < 0 -> endBehavior
+ offsetAvailable > 0f -> startBehavior
+ offsetAvailable < 0f -> endBehavior
else -> return@PriorityNestedScrollConnection false
}
- val isZeroOffset = offsetBeforeStart == Offset.Zero
+ val isZeroOffset = offsetBeforeStart == 0f
when (behavior) {
NestedScrollBehavior.DuringTransitionBetweenScenes -> {
@@ -619,56 +614,57 @@ class SceneNestedScrollHandler(
}
NestedScrollBehavior.EdgeNoOverscroll -> {
canChangeScene = isZeroOffset
- isZeroOffset && hasNextScene(amount)
+ isZeroOffset && hasNextScene(offsetAvailable)
}
NestedScrollBehavior.EdgeWithOverscroll -> {
canChangeScene = isZeroOffset
- hasNextScene(amount)
+ hasNextScene(offsetAvailable)
}
NestedScrollBehavior.Always -> {
canChangeScene = true
- hasNextScene(amount)
+ hasNextScene(offsetAvailable)
}
}
},
canStartPostFling = { velocityAvailable ->
- val amount = velocityAvailable.toAmount()
val behavior: NestedScrollBehavior =
when {
- amount > 0 -> startBehavior
- amount < 0 -> endBehavior
+ velocityAvailable > 0f -> startBehavior
+ velocityAvailable < 0f -> endBehavior
else -> return@PriorityNestedScrollConnection false
}
// We could start an overscroll animation
canChangeScene = false
- behavior.canStartOnPostFling && hasNextScene(amount)
+ behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
},
canContinueScroll = { true },
onStart = {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null)
+ gestureHandler.onDragStarted(
+ pointersDown = 1,
+ layoutSize = gestureHandler.currentScene.targetSize,
+ startedPosition = null,
+ )
},
onScroll = { offsetAvailable ->
if (gestureHandler.gestureWithPriority != this) {
- return@PriorityNestedScrollConnection Offset.Zero
+ return@PriorityNestedScrollConnection 0f
}
- val amount = offsetAvailable.toAmount()
-
// TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
// initiated in a nested child.
- gestureHandler.onDrag(amount)
+ gestureHandler.onDrag(offsetAvailable)
- amount.toOffset()
+ offsetAvailable
},
onStop = { velocityAvailable ->
if (gestureHandler.gestureWithPriority != this) {
- return@PriorityNestedScrollConnection Velocity.Zero
+ return@PriorityNestedScrollConnection 0f
}
gestureHandler.onDragStopped(
- velocity = velocityAvailable.toAmount(),
+ velocity = velocityAvailable,
canChangeScene = canChangeScene
)
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 efdfe7a7921e..07add77eccd4 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
@@ -16,12 +16,15 @@
package com.android.compose.animation.scene
+import androidx.annotation.FloatRange
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.platform.LocalDensity
@@ -41,6 +44,8 @@ import androidx.compose.ui.platform.LocalDensity
* @param transitions the definition of the transitions used to animate a change of scene.
* @param state the observable state of this layout.
* @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
+ * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
+ * intercepted, the progress value must be above the threshold, and below (1 - threshold).
* @param scenes the configuration of the different scenes of this layout.
*/
@Composable
@@ -51,30 +56,20 @@ fun SceneTransitionLayout(
modifier: Modifier = Modifier,
state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
edgeDetector: EdgeDetector = DefaultEdgeDetector,
+ @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
- val density = LocalDensity.current
- val coroutineScope = rememberCoroutineScope()
- val layoutImpl = remember {
- SceneTransitionLayoutImpl(
- onChangeScene = onChangeScene,
- builder = scenes,
- transitions = transitions,
- state = state,
- density = density,
- edgeDetector = edgeDetector,
- coroutineScope = coroutineScope,
- )
- }
-
- layoutImpl.onChangeScene = onChangeScene
- layoutImpl.transitions = transitions
- layoutImpl.density = density
- layoutImpl.edgeDetector = edgeDetector
-
- layoutImpl.setScenes(scenes)
- layoutImpl.setCurrentScene(currentScene)
- layoutImpl.Content(modifier)
+ SceneTransitionLayoutForTesting(
+ currentScene,
+ onChangeScene,
+ transitions,
+ state,
+ edgeDetector,
+ transitionInterceptionThreshold,
+ modifier,
+ onLayoutImpl = null,
+ scenes,
+ )
}
interface SceneTransitionLayoutScope {
@@ -101,7 +96,11 @@ interface SceneTransitionLayoutScope {
@DslMarker annotation class ElementDsl
@ElementDsl
+@Stable
interface SceneScope {
+ /** The state of the [SceneTransitionLayout] in which this scene is contained. */
+ val layoutState: SceneTransitionLayoutState
+
/**
* Tag an element identified by [key].
*
@@ -181,6 +180,18 @@ interface SceneScope {
lerp: (start: T, stop: T, fraction: Float) -> T,
canOverflow: Boolean,
): State<T>
+
+ /**
+ * Punch a hole in this [element] using the bounds of [bounds] in [scene] and the given [shape].
+ *
+ * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area.
+ * This can be used to make content drawn below an opaque element visible. For example, if we
+ * have [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below
+ * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big
+ * clock time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be
+ * the result.
+ */
+ fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier
}
// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
@@ -222,3 +233,47 @@ enum class SwipeDirection(val orientation: Orientation) {
Left(Orientation.Horizontal),
Right(Orientation.Horizontal),
}
+
+/**
+ * An internal version of [SceneTransitionLayout] to be used for tests.
+ *
+ * Important: You should use this only in tests and if you need to access the underlying
+ * [SceneTransitionLayoutImpl]. In other cases, you should use [SceneTransitionLayout].
+ */
+@Composable
+internal fun SceneTransitionLayoutForTesting(
+ currentScene: SceneKey,
+ onChangeScene: (SceneKey) -> Unit,
+ transitions: SceneTransitions,
+ state: SceneTransitionLayoutState,
+ edgeDetector: EdgeDetector,
+ transitionInterceptionThreshold: Float,
+ modifier: Modifier,
+ onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)?,
+ scenes: SceneTransitionLayoutScope.() -> Unit,
+) {
+ val density = LocalDensity.current
+ val coroutineScope = rememberCoroutineScope()
+ val layoutImpl = remember {
+ SceneTransitionLayoutImpl(
+ onChangeScene = onChangeScene,
+ builder = scenes,
+ transitions = transitions,
+ state = state,
+ density = density,
+ edgeDetector = edgeDetector,
+ transitionInterceptionThreshold = transitionInterceptionThreshold,
+ coroutineScope = coroutineScope,
+ )
+ .also { onLayoutImpl?.invoke(it) }
+ }
+
+ layoutImpl.onChangeScene = onChangeScene
+ layoutImpl.transitions = transitions
+ layoutImpl.density = density
+ layoutImpl.edgeDetector = edgeDetector
+
+ layoutImpl.setScenes(scenes)
+ layoutImpl.setCurrentScene(currentScene)
+ layoutImpl.Content(modifier)
+}
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 6edd1b6b923d..02ddccbc051b 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
@@ -17,37 +17,40 @@
package com.android.compose.animation.scene
import androidx.activity.compose.BackHandler
-import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.layout.LookaheadScope
-import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
+import com.android.compose.ui.util.lerp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
-@VisibleForTesting
-class SceneTransitionLayoutImpl(
+@Stable
+internal class SceneTransitionLayoutImpl(
onChangeScene: (SceneKey) -> Unit,
builder: SceneTransitionLayoutScope.() -> Unit,
transitions: SceneTransitions,
internal val state: SceneTransitionLayoutState,
density: Density,
edgeDetector: EdgeDetector,
+ transitionInterceptionThreshold: Float,
coroutineScope: CoroutineScope,
) {
internal val scenes = SnapshotStateMap<SceneKey, Scene>()
@@ -60,16 +63,11 @@ class SceneTransitionLayoutImpl(
internal var transitions by mutableStateOf(transitions)
internal var density: Density by mutableStateOf(density)
internal var edgeDetector by mutableStateOf(edgeDetector)
+ internal var transitionInterceptionThreshold by mutableStateOf(transitionInterceptionThreshold)
private val horizontalGestureHandler: SceneGestureHandler
private val verticalGestureHandler: SceneGestureHandler
- /**
- * The size of this layout. Note that this could be [IntSize.Zero] if this layour does not have
- * any scene configured or right before the first measure pass of the layout.
- */
- @VisibleForTesting var size by mutableStateOf(IntSize.Zero)
-
init {
setScenes(builder)
@@ -157,15 +155,46 @@ class SceneTransitionLayoutImpl(
}
@Composable
+ @OptIn(ExperimentalComposeUiApi::class)
internal fun Content(modifier: Modifier) {
Box(
modifier
// Handle horizontal and vertical swipes on this layout.
// Note: order here is important and will give a slight priority to the vertical
// swipes.
- .swipeToScene(gestureHandler(Orientation.Horizontal))
- .swipeToScene(gestureHandler(Orientation.Vertical))
- .onSizeChanged { size = it }
+ .swipeToScene(horizontalGestureHandler)
+ .swipeToScene(verticalGestureHandler)
+ // Animate the size of this layout.
+ .intermediateLayout { measurable, constraints ->
+ // Measure content normally.
+ val placeable = measurable.measure(constraints)
+
+ val width: Int
+ val height: Int
+ val state = state.transitionState
+ if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+ width = placeable.width
+ height = placeable.height
+ } else {
+ // Interpolate the size.
+ val fromSize = scene(state.fromScene).targetSize
+ val toSize = scene(state.toScene).targetSize
+
+ // Optimization: make sure we don't read state.progress if fromSize ==
+ // toSize to avoid running this code every frame when the layout size does
+ // not change.
+ if (fromSize == toSize) {
+ width = fromSize.width
+ height = fromSize.height
+ } else {
+ val size = lerp(fromSize, toSize, state.progress)
+ width = size.width.coerceAtLeast(0)
+ height = size.height.coerceAtLeast(0)
+ }
+ }
+
+ layout(width, height) { placeable.place(0, 0) }
+ }
) {
LookaheadScope {
val scenesToCompose =
@@ -230,4 +259,8 @@ class SceneTransitionLayoutImpl(
}
internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene)
+
+ internal fun setScenesTargetSizeForTest(size: IntSize) {
+ scenes.values.forEach { it.targetSize = size }
+ }
}
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 b9f83c545122..f48e9147eef4 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
@@ -16,11 +16,13 @@
package com.android.compose.animation.scene
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
/** The state of a [SceneTransitionLayout]. */
+@Stable
class SceneTransitionLayoutState(initialScene: SceneKey) {
/**
* The current [TransitionState]. All values read here are backed by the Snapshot system.
@@ -29,9 +31,31 @@ class SceneTransitionLayoutState(initialScene: SceneKey) {
* [SceneTransitionLayoutState.observableTransitionState] instead.
*/
var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene))
- internal set
+
+ /**
+ * Whether we are transitioning, optionally restricting the check to the transition between
+ * [from] and [to].
+ */
+ fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+ val transition = transitionState as? TransitionState.Transition ?: return false
+
+ // TODO(b/310915136): Remove this check.
+ if (transition.fromScene == transition.toScene) {
+ return false
+ }
+
+ return (from == null || transition.fromScene == from) &&
+ (to == null || transition.toScene == to)
+ }
+
+ /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
+ fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
+ return isTransitioning(from = scene, to = other) ||
+ isTransitioning(from = other, to = scene)
+ }
}
+@Stable
sealed interface TransitionState {
/**
* The current effective scene. If a new transition was triggered, it would start from 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 b163a2a96039..f91895bb0e05 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
@@ -16,18 +16,18 @@
package com.android.compose.animation.scene
-import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.snap
+import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
+import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
-import com.android.compose.animation.scene.transformation.ModifierTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
@@ -37,12 +37,11 @@ import com.android.compose.animation.scene.transformation.Translate
/** The transitions configuration of a [SceneTransitionLayout]. */
class SceneTransitions(
- @get:VisibleForTesting val transitionSpecs: List<TransitionSpec>,
+ internal val transitionSpecs: List<TransitionSpec>,
) {
private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>()
- @VisibleForTesting
- fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
+ internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
return cache.getOrPut(from) { mutableMapOf() }.getOrPut(to) { findSpec(from, to) }
}
@@ -94,6 +93,7 @@ class SceneTransitions(
}
/** The definition of a transition between [from] and [to]. */
+@Stable
data class TransitionSpec(
val from: SceneKey?,
val to: SceneKey?,
@@ -123,9 +123,9 @@ data class TransitionSpec(
scene: SceneKey,
): ElementTransformations {
var shared: SharedElementTransformation? = null
- val modifier = mutableListOf<ModifierTransformation>()
var offset: PropertyTransformation<Offset>? = null
var size: PropertyTransformation<IntSize>? = null
+ var drawScale: PropertyTransformation<Scale>? = null
var alpha: PropertyTransformation<Float>? = null
fun <T> onPropertyTransformation(
@@ -144,6 +144,10 @@ data class TransitionSpec(
throwIfNotNull(size, element, name = "size")
size = root as PropertyTransformation<IntSize>
}
+ is DrawScale -> {
+ throwIfNotNull(drawScale, element, name = "drawScale")
+ drawScale = root as PropertyTransformation<Scale>
+ }
is Fade -> {
throwIfNotNull(alpha, element, name = "alpha")
alpha = root as PropertyTransformation<Float>
@@ -162,12 +166,11 @@ data class TransitionSpec(
throwIfNotNull(shared, element, name = "shared")
shared = transformation
}
- is ModifierTransformation -> modifier.add(transformation)
is PropertyTransformation<*> -> onPropertyTransformation(transformation)
}
}
- return ElementTransformations(shared, modifier, offset, size, alpha)
+ return ElementTransformations(shared, offset, size, drawScale, alpha)
}
private fun throwIfNotNull(
@@ -184,8 +187,8 @@ data class TransitionSpec(
/** The transformations of an element during a transition. */
internal class ElementTransformations(
val shared: SharedElementTransformation?,
- val modifier: List<ModifierTransformation>,
val offset: PropertyTransformation<Offset>?,
val size: PropertyTransformation<IntSize>?,
+ val drawScale: PropertyTransformation<Scale>?,
val alpha: PropertyTransformation<Float>?,
)
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 7b7ddfa5ec4e..f820074ec3d1 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
@@ -17,8 +17,7 @@
package com.android.compose.animation.scene
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -130,19 +129,6 @@ interface TransitionBuilder : PropertyTransformationBuilder {
)
/**
- * Punch a hole in the element(s) matching [matcher] that has the same bounds as [bounds] and
- * using the given [shape].
- *
- * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area.
- * This can be used to make content drawn below an opaque element visible. For example, if we
- * have [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below
- * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big
- * clock time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be
- * the result.
- */
- fun punchHole(matcher: ElementMatcher, bounds: ElementKey, shape: Shape = RectangleShape)
-
- /**
* Adds the transformations in [builder] but in reversed order. This allows you to partially
* reuse the definition of the transition from scene `Foo` to scene `Bar` inside the definition
* of the transition from scene `Bar` to scene `Foo`.
@@ -224,12 +210,22 @@ interface PropertyTransformationBuilder {
/**
* Scale the [width] and [height] of the element(s) matching [matcher]. Note that this scaling
* is done during layout, so it will potentially impact the size and position of other elements.
- *
- * TODO(b/290184746): Also provide a scaleDrawing() to scale an element at drawing time.
*/
fun scaleSize(matcher: ElementMatcher, width: Float = 1f, height: Float = 1f)
/**
+ * Scale the drawing with [scaleX] and [scaleY] of the element(s) matching [matcher]. Note this
+ * will only scale the draw inside of an element, therefore it won't impact layout of elements
+ * around it.
+ */
+ fun scaleDraw(
+ matcher: ElementMatcher,
+ scaleX: Float = 1f,
+ scaleY: Float = 1f,
+ pivot: Offset = Offset.Unspecified
+ )
+
+ /**
* Scale the element(s) matching [matcher] so that it grows/shrinks to the same size as [anchor]
* .
*
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 d2bfd91842ae..8c0a5a394331 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
@@ -21,14 +21,14 @@ import androidx.compose.animation.core.DurationBasedAnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Dp
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
+import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.PropertyTransformation
-import com.android.compose.animation.scene.transformation.PunchHole
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
@@ -91,10 +91,6 @@ internal class TransitionBuilderImpl : TransitionBuilder {
spec.vectorize(Float.VectorConverter).durationMillis
}
- override fun punchHole(matcher: ElementMatcher, bounds: ElementKey, shape: Shape) {
- transformations.add(PunchHole(matcher, bounds, shape))
- }
-
override fun reversed(builder: TransitionBuilder.() -> Unit) {
reversed = true
builder()
@@ -178,6 +174,10 @@ internal class TransitionBuilderImpl : TransitionBuilder {
transformation(ScaleSize(matcher, width, height))
}
+ override fun scaleDraw(matcher: ElementMatcher, scaleX: Float, scaleY: Float, pivot: Offset) {
+ transformation(DrawScale(matcher, scaleX, scaleY, pivot))
+ }
+
override fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey) {
transformation(AnchoredSize(matcher, anchor))
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
new file mode 100644
index 000000000000..d1cf8ee6ad4a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -0,0 +1,48 @@
+/*
+ * 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 com.android.compose.animation.scene.transformation
+
+import androidx.compose.ui.geometry.Offset
+import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementMatcher
+import com.android.compose.animation.scene.Scale
+import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.TransitionState
+
+/**
+ * Scales the draw size of an element. Note this will only scale the draw inside of an element,
+ * therefore it won't impact layout of elements around it.
+ */
+internal class DrawScale(
+ override val matcher: ElementMatcher,
+ private val scaleX: Float,
+ private val scaleY: Float,
+ private val pivot: Offset = Offset.Unspecified,
+) : PropertyTransformation<Scale> {
+
+ override fun transform(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: Scene,
+ element: Element,
+ sceneValues: Element.TargetValues,
+ transition: TransitionState.Transition,
+ value: Scale,
+ ): Scale {
+ return Scale(scaleX, scaleY, pivot)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 840800d838db..70534dde4f6f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -38,7 +38,7 @@ internal class EdgeTranslate(
transition: TransitionState.Transition,
value: Offset
): Offset {
- val sceneSize = scene.size
+ val sceneSize = scene.targetSize
val elementSize = sceneValues.targetSize
if (elementSize == Element.SizeUnspecified) {
return value
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
deleted file mode 100644
index 62d67f03f1d0..000000000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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 com.android.compose.animation.scene.transformation
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.toRect
-import androidx.compose.ui.graphics.BlendMode
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Paint
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.DrawScope
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
-import androidx.compose.ui.graphics.drawscope.translate
-import androidx.compose.ui.graphics.withSaveLayer
-import androidx.compose.ui.unit.toSize
-import com.android.compose.animation.scene.Element
-import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-
-/** Punch a hole in an element using the bounds of another element and a given [shape]. */
-internal class PunchHole(
- override val matcher: ElementMatcher,
- private val bounds: ElementKey,
- private val shape: Shape,
-) : ModifierTransformation {
- override fun Modifier.transform(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
- element: Element,
- sceneValues: Element.TargetValues,
- ): Modifier {
- return drawWithContent {
- val bounds = layoutImpl.elements[bounds]
- if (
- bounds == null ||
- bounds.lastSharedValues.size == Element.SizeUnspecified ||
- bounds.lastSharedValues.offset == Offset.Unspecified
- ) {
- drawContent()
- return@drawWithContent
- }
-
- drawIntoCanvas { canvas ->
- canvas.withSaveLayer(size.toRect(), Paint()) {
- drawContent()
-
- val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset
- translate(offset.x, offset.y) { drawHole(bounds) }
- }
- }
- }
- }
-
- private fun DrawScope.drawHole(bounds: Element) {
- val boundsSize = bounds.lastSharedValues.size.toSize()
- if (shape == RectangleShape) {
- drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut)
- return
- }
-
- // TODO(b/290184746): Cache outline if the size of bounds does not change.
- drawOutline(
- shape.createOutline(
- boundsSize,
- layoutDirection,
- this,
- ),
- Color.Black,
- blendMode = BlendMode.DstOut,
- )
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 0db8469466ef..206935558179 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene.transformation
-import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scene
@@ -52,19 +51,6 @@ internal class SharedElementTransformation(
internal val scenePicker: SharedElementScenePicker,
) : Transformation
-/** A transformation that is applied on the element during the whole transition. */
-internal interface ModifierTransformation : Transformation {
- /** Apply the transformation to [element]. */
- // TODO(b/290184746): Figure out a public API for custom transformations that don't have access
- // to these internal classes.
- fun Modifier.transform(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
- element: Element,
- sceneValues: Element.TargetValues,
- ): Modifier
-}
-
/** A transformation that changes the value of an element property, like its size or offset. */
internal sealed interface PropertyTransformation<T> : Transformation {
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 824c10b88a9b..a5fd1bfb72e6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,10 +16,12 @@
package com.android.compose.nestedscroll
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
+import com.android.compose.ui.util.SpaceVectorConverter
/**
* This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and
@@ -147,3 +149,35 @@ class PriorityNestedScrollConnection(
return onStop(velocity)
}
}
+
+fun PriorityNestedScrollConnection(
+ orientation: Orientation,
+ canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+ canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+ canStartPostFling: (velocityAvailable: Float) -> Boolean,
+ canContinueScroll: () -> Boolean,
+ onStart: () -> Unit,
+ onScroll: (offsetAvailable: Float) -> Float,
+ onStop: (velocityAvailable: Float) -> Float,
+) =
+ with(SpaceVectorConverter(orientation)) {
+ PriorityNestedScrollConnection(
+ canStartPreScroll = { offsetAvailable: Offset, offsetBeforeStart: Offset ->
+ canStartPreScroll(offsetAvailable.toFloat(), offsetBeforeStart.toFloat())
+ },
+ canStartPostScroll = { offsetAvailable: Offset, offsetBeforeStart: Offset ->
+ canStartPostScroll(offsetAvailable.toFloat(), offsetBeforeStart.toFloat())
+ },
+ canStartPostFling = { velocityAvailable: Velocity ->
+ canStartPostFling(velocityAvailable.toFloat())
+ },
+ canContinueScroll = canContinueScroll,
+ onStart = onStart,
+ onScroll = { offsetAvailable: Offset ->
+ onScroll(offsetAvailable.toFloat()).toOffset()
+ },
+ onStop = { velocityAvailable: Velocity ->
+ onStop(velocityAvailable.toFloat()).toVelocity()
+ },
+ )
+ }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
index eb1a634ff491..13747b72724e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
@@ -17,7 +17,10 @@
package com.android.compose.ui.util
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.Scale
import kotlin.math.roundToInt
import kotlin.math.roundToLong
@@ -43,3 +46,19 @@ fun lerp(start: IntSize, stop: IntSize, fraction: Float): IntSize {
lerp(start.height, stop.height, fraction)
)
}
+
+/** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
+fun lerp(start: Scale, stop: Scale, fraction: Float): Scale {
+ val pivot =
+ when {
+ start.pivot.isSpecified && stop.pivot.isSpecified ->
+ lerp(start.pivot, stop.pivot, fraction)
+ start.pivot.isSpecified -> start.pivot
+ else -> stop.pivot
+ }
+ return Scale(
+ lerp(start.scaleX, stop.scaleX, fraction),
+ lerp(start.scaleY, stop.scaleY, fraction),
+ pivot
+ )
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
new file mode 100644
index 000000000000..a13e9441523a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.compose.ui.util
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Velocity
+
+interface SpaceVectorConverter {
+ fun Offset.toFloat(): Float
+ fun Velocity.toFloat(): Float
+ fun Float.toOffset(): Offset
+ fun Float.toVelocity(): Velocity
+}
+
+fun SpaceVectorConverter(orientation: Orientation) =
+ when (orientation) {
+ Orientation.Horizontal -> HorizontalConverter
+ Orientation.Vertical -> VerticalConverter
+ }
+
+private val HorizontalConverter =
+ object : SpaceVectorConverter {
+ override fun Offset.toFloat() = x
+ override fun Velocity.toFloat() = x
+ override fun Float.toOffset() = Offset(this, 0f)
+ override fun Float.toVelocity() = Velocity(this, 0f)
+ }
+
+private val VerticalConverter =
+ object : SpaceVectorConverter {
+ override fun Offset.toFloat() = y
+ override fun Velocity.toFloat() = y
+ override fun Float.toOffset() = Offset(0f, this)
+ override fun Float.toVelocity() = Velocity(0f, this)
+ }
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 6de75501dd34..13df35b7e1e8 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -30,10 +30,13 @@ android_test {
srcs: [
"src/**/*.kt",
+
+ // TODO(b/240432457): Depend on PlatformComposeSceneTransitionLayout
+ // directly once Kotlin tests can access internal declarations.
+ ":PlatformComposeSceneTransitionLayout-srcs",
],
static_libs: [
- "PlatformComposeSceneTransitionLayout",
"PlatformComposeSceneTransitionLayoutTestsUtils",
"androidx.test.runner",
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
new file mode 100644
index 000000000000..ce3e1db2c3d0
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -0,0 +1,457 @@
+/*
+ * 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.compose.animation.scene
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Composable
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun SceneScope.Element(
+ key: ElementKey,
+ size: Dp,
+ offset: Dp,
+ modifier: Modifier = Modifier,
+ onLayout: () -> Unit = {},
+ onPlacement: () -> Unit = {},
+ ) {
+ Box(
+ modifier
+ .offset(offset)
+ .element(key)
+ .intermediateLayout { measurable, constraints ->
+ onLayout()
+ val placement = measurable.measure(constraints)
+ layout(placement.width, placement.height) {
+ onPlacement()
+ placement.place(0, 0)
+ }
+ }
+ .size(size)
+ )
+ }
+
+ @Test
+ fun staticElements_noLayout_noPlacement() {
+ val nFrames = 20
+ val layoutSize = 100.dp
+ val elementSize = 50.dp
+ val elementOffset = 20.dp
+
+ var fooLayouts = 0
+ var fooPlacements = 0
+ var barLayouts = 0
+ var barPlacements = 0
+
+ rule.testTransition(
+ fromSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(
+ TestElements.Foo,
+ elementSize,
+ elementOffset,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
+
+ // Transformed element
+ Element(
+ TestElements.Bar,
+ elementSize,
+ elementOffset,
+ onLayout = { barLayouts++ },
+ onPlacement = { barPlacements++ },
+ )
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(TestElements.Foo, elementSize, elementOffset)
+ }
+ },
+ transition = {
+ spec = tween(nFrames * 16)
+
+ // no-op transformations.
+ translate(TestElements.Bar, x = 0.dp, y = 0.dp)
+ scaleSize(TestElements.Bar, width = 1f, height = 1f)
+ },
+ ) {
+ var numberOfLayoutsAfterOneAnimationFrame = 0
+ var numberOfPlacementsAfterOneAnimationFrame = 0
+
+ fun assertNumberOfLayoutsAndPlacements() {
+ assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(fooPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(barPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
+ }
+
+ at(16) {
+ // Capture the number of layouts and placements that happened after 1 animation
+ // frame.
+ numberOfLayoutsAfterOneAnimationFrame = fooLayouts
+ numberOfPlacementsAfterOneAnimationFrame = fooPlacements
+ }
+ repeat(nFrames - 2) { i ->
+ // Ensure that all animation frames (except the final one) don't relayout or replace
+ // static (shared or transformed) elements.
+ at(32L + i * 16) { assertNumberOfLayoutsAndPlacements() }
+ }
+ }
+ }
+
+ @Test
+ fun onlyMovingElements_noLayout_onlyPlacement() {
+ val nFrames = 20
+ val layoutSize = 100.dp
+ val elementSize = 50.dp
+
+ var fooLayouts = 0
+ var fooPlacements = 0
+ var barLayouts = 0
+ var barPlacements = 0
+
+ rule.testTransition(
+ fromSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(
+ TestElements.Foo,
+ elementSize,
+ offset = 0.dp,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
+
+ // Transformed element
+ Element(
+ TestElements.Bar,
+ elementSize,
+ offset = 0.dp,
+ onLayout = { barLayouts++ },
+ onPlacement = { barPlacements++ },
+ )
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(TestElements.Foo, elementSize, offset = 20.dp)
+ }
+ },
+ transition = {
+ spec = tween(nFrames * 16)
+
+ // Only translate Bar.
+ translate(TestElements.Bar, x = 20.dp, y = 20.dp)
+ scaleSize(TestElements.Bar, width = 1f, height = 1f)
+ },
+ ) {
+ var numberOfLayoutsAfterOneAnimationFrame = 0
+ var lastNumberOfPlacements = 0
+
+ fun assertNumberOfLayoutsAndPlacements() {
+ // The number of layouts have not changed.
+ assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+
+ // The number of placements have increased.
+ assertThat(fooPlacements).isGreaterThan(lastNumberOfPlacements)
+ assertThat(barPlacements).isGreaterThan(lastNumberOfPlacements)
+ lastNumberOfPlacements = fooPlacements
+ }
+
+ at(16) {
+ // Capture the number of layouts and placements that happened after 1 animation
+ // frame.
+ numberOfLayoutsAfterOneAnimationFrame = fooLayouts
+ lastNumberOfPlacements = fooPlacements
+ }
+ repeat(nFrames - 2) { i ->
+ // Ensure that all animation frames (except the final one) only replaced the
+ // elements.
+ at(32L + i * 16) { assertNumberOfLayoutsAndPlacements() }
+ }
+ }
+ }
+
+ @Test
+ fun elementIsReusedInSameSceneAndBetweenScenes() {
+ var currentScene by mutableStateOf(TestScenes.SceneA)
+ var sceneCState by mutableStateOf(0)
+ var sceneDState by mutableStateOf(0)
+ val key = TestElements.Foo
+ var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
+
+ rule.setContent {
+ SceneTransitionLayoutForTesting(
+ currentScene = currentScene,
+ onChangeScene = { currentScene = it },
+ transitions = remember { transitions {} },
+ state = remember { SceneTransitionLayoutState(currentScene) },
+ edgeDetector = DefaultEdgeDetector,
+ modifier = Modifier,
+ transitionInterceptionThreshold = 0f,
+ onLayoutImpl = { nullableLayoutImpl = it },
+ ) {
+ scene(TestScenes.SceneA) { /* Nothing */}
+ scene(TestScenes.SceneB) { Box(Modifier.element(key)) }
+ 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 */
+ }
+ }
+ }
+ }
+ }
+
+ assertThat(nullableLayoutImpl).isNotNull()
+ val layoutImpl = nullableLayoutImpl!!
+
+ // Scene A: no elements in the elements map.
+ rule.waitForIdle()
+ assertThat(layoutImpl.elements).isEmpty()
+
+ // Scene B: element is in the map.
+ currentScene = TestScenes.SceneB
+ rule.waitForIdle()
+
+ assertThat(layoutImpl.elements.keys).containsExactly(key)
+ val element = layoutImpl.elements.getValue(key)
+ assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneB)
+
+ // Scene C, state 0: the same element is reused.
+ currentScene = TestScenes.SceneC
+ sceneCState = 0
+ rule.waitForIdle()
+
+ assertThat(layoutImpl.elements.keys).containsExactly(key)
+ assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
+ assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneC)
+
+ // Scene C, state 1: the same element is reused.
+ sceneCState = 1
+ rule.waitForIdle()
+
+ assertThat(layoutImpl.elements.keys).containsExactly(key)
+ assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
+ assertThat(element.sceneValues.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.sceneValues.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.sceneValues.keys).containsExactly(TestScenes.SceneD)
+
+ // Scene D, state 2: the element is removed from the map.
+ sceneDState = 2
+ rule.waitForIdle()
+
+ assertThat(element.sceneValues).isEmpty()
+ assertThat(layoutImpl.elements).isEmpty()
+ }
+
+ @Test
+ fun throwsExceptionWhenElementIsComposedMultipleTimes() {
+ val key = TestElements.Foo
+
+ assertThrows(IllegalStateException::class.java) {
+ rule.setContent {
+ TestSceneScope {
+ Column {
+ Box(Modifier.element(key))
+ Box(Modifier.element(key))
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun throwsExceptionWhenElementIsComposedMultipleTimes_childModifier() {
+ val key = TestElements.Foo
+
+ assertThrows(IllegalStateException::class.java) {
+ rule.setContent {
+ TestSceneScope {
+ Column {
+ val childModifier = Modifier.element(key)
+ Box(childModifier)
+ Box(childModifier)
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun throwsExceptionWhenElementIsComposedMultipleTimes_childModifier_laterDuplication() {
+ val key = TestElements.Foo
+
+ assertThrows(IllegalStateException::class.java) {
+ var nElements by mutableStateOf(1)
+ rule.setContent {
+ TestSceneScope {
+ Column {
+ val childModifier = Modifier.element(key)
+ repeat(nElements) { Box(childModifier) }
+ }
+ }
+ }
+
+ nElements = 2
+ rule.waitForIdle()
+ }
+ }
+
+ @Test
+ fun throwsExceptionWhenElementIsComposedMultipleTimes_updatedNode() {
+ assertThrows(IllegalStateException::class.java) {
+ var key by mutableStateOf(TestElements.Foo)
+ rule.setContent {
+ TestSceneScope {
+ Column {
+ Box(Modifier.element(key))
+ Box(Modifier.element(TestElements.Bar))
+ }
+ }
+ }
+
+ key = TestElements.Bar
+ rule.waitForIdle()
+ }
+ }
+
+ @Test
+ fun elementModifierSupportsUpdates() {
+ var key by mutableStateOf(TestElements.Foo)
+ var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
+
+ rule.setContent {
+ SceneTransitionLayoutForTesting(
+ currentScene = TestScenes.SceneA,
+ onChangeScene = {},
+ transitions = remember { transitions {} },
+ state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
+ edgeDetector = DefaultEdgeDetector,
+ modifier = Modifier,
+ transitionInterceptionThreshold = 0f,
+ onLayoutImpl = { nullableLayoutImpl = it },
+ ) {
+ scene(TestScenes.SceneA) { Box(Modifier.element(key)) }
+ }
+ }
+
+ assertThat(nullableLayoutImpl).isNotNull()
+ val layoutImpl = nullableLayoutImpl!!
+
+ // There is only Foo in the elements map.
+ assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
+ val fooElement = layoutImpl.elements.getValue(TestElements.Foo)
+ assertThat(fooElement.sceneValues.keys).containsExactly(TestScenes.SceneA)
+
+ key = TestElements.Bar
+
+ // There is only Bar in the elements map and foo scene values was cleaned up.
+ rule.waitForIdle()
+ assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Bar)
+ val barElement = layoutImpl.elements.getValue(TestElements.Bar)
+ assertThat(barElement.sceneValues.keys).containsExactly(TestScenes.SceneA)
+ assertThat(fooElement.sceneValues).isEmpty()
+ }
+
+ @Test
+ fun existingElementsDontRecomposeWhenTransitionStateChanges() {
+ var fooCompositions = 0
+
+ rule.testTransition(
+ fromSceneContent = {
+ SideEffect { fooCompositions++ }
+ Box(Modifier.element(TestElements.Foo))
+ },
+ toSceneContent = {},
+ transition = {
+ spec = tween(4 * 16)
+
+ scaleSize(TestElements.Foo, width = 2f, height = 0.5f)
+ translate(TestElements.Foo, x = 10.dp, y = 10.dp)
+ fade(TestElements.Foo)
+ }
+ ) {
+ before { assertThat(fooCompositions).isEqualTo(1) }
+ at(16) { assertThat(fooCompositions).isEqualTo(1) }
+ at(32) { assertThat(fooCompositions).isEqualTo(1) }
+ at(48) { assertThat(fooCompositions).isEqualTo(1) }
+ after { assertThat(fooCompositions).isEqualTo(1) }
+ }
+ }
+}
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 1e3d01108103..49ef31b16d73 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
@@ -46,6 +46,7 @@ import org.junit.Test
import org.junit.runner.RunWith
private const val SCREEN_SIZE = 100f
+private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt())
@RunWith(AndroidJUnit4::class)
class SceneGestureHandlerTest {
@@ -68,6 +69,8 @@ class SceneGestureHandlerTest {
scene(SceneC) { Text("SceneC") }
}
+ val transitionInterceptionThreshold = 0.05f
+
val sceneGestureHandler =
SceneGestureHandler(
layoutImpl =
@@ -78,9 +81,10 @@ class SceneGestureHandlerTest {
state = layoutState,
density = Density(1f),
edgeDetector = DefaultEdgeDetector,
+ transitionInterceptionThreshold = transitionInterceptionThreshold,
coroutineScope = coroutineScope,
)
- .also { it.size = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt()) },
+ .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) },
orientation = Orientation.Vertical,
coroutineScope = coroutineScope,
)
@@ -106,6 +110,9 @@ class SceneGestureHandlerTest {
val transitionState: TransitionState
get() = layoutState.transitionState
+ val progress: Float
+ get() = (transitionState as Transition).progress
+
fun advanceUntilIdle() {
coroutineScope.testScheduler.advanceUntilIdle()
}
@@ -128,31 +135,33 @@ class SceneGestureHandlerTest {
runMonotonicClockTest { TestGestureScope(coroutineScope = this).block() }
}
+ private fun DraggableHandler.onDragStarted() =
+ onDragStarted(layoutSize = LAYOUT_SIZE, startedPosition = Offset.Zero)
+
@Test
fun testPreconditions() = runGestureTest { assertScene(currentScene = SceneA, isIdle = true) }
@Test
fun onDragStarted_shouldStartATransition() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
}
@Test
fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
draggable.onDelta(pixels = deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
draggable.onDelta(pixels = deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
}
@Test
fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -170,7 +179,7 @@ class SceneGestureHandlerTest {
@Test
fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -188,7 +197,7 @@ class SceneGestureHandlerTest {
@Test
fun onDragStoppedAfterStarted_returnImmediatelyToIdle() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDragStopped(velocity = 0f)
@@ -197,7 +206,7 @@ class SceneGestureHandlerTest {
@Test
fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -217,7 +226,7 @@ class SceneGestureHandlerTest {
assertScene(currentScene = SceneC, isIdle = false)
// Start a new gesture while the offset is animating
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertThat(sceneGestureHandler.isAnimatingOffset).isFalse()
}
@@ -253,8 +262,7 @@ class SceneGestureHandlerTest {
)
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
assertThat(consumed).isEqualTo(offsetY10)
}
@@ -278,13 +286,12 @@ class SceneGestureHandlerTest {
nestedScroll.scroll(available = offsetY10)
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
// start intercept preScroll
val consumed =
nestedScroll.onPreScroll(available = offsetY10, source = NestedScrollSource.Drag)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
// do nothing on postScroll
nestedScroll.onPostScroll(
@@ -292,13 +299,71 @@ class SceneGestureHandlerTest {
available = Offset.Zero,
source = NestedScrollSource.Drag
)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.3f)
assertScene(currentScene = SceneA, isIdle = false)
}
+ private suspend fun TestGestureScope.preScrollAfterSceneTransition(
+ firstScroll: Float,
+ secondScroll: Float
+ ) {
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ // start scene transition
+ nestedScroll.scroll(available = Offset(0f, SCREEN_SIZE * firstScroll))
+
+ // stop scene transition (start the "stop animation")
+ nestedScroll.onPreFling(available = Velocity.Zero)
+
+ // a pre scroll event, that could be intercepted by SceneGestureHandler
+ nestedScroll.onPreScroll(Offset(0f, SCREEN_SIZE * secondScroll), NestedScrollSource.Drag)
+ }
+
+ // Float tolerance for comparisons
+ private val tolerance = 0.00001f
+
+ @Test
+ fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest {
+ val first = transitionInterceptionThreshold - tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertScene(SceneA, isIdle = true)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMinInterceptable_interceptPreScrollEvents() = runGestureTest {
+ val first = transitionInterceptionThreshold + tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertThat(progress).isWithin(tolerance).of(first + second)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMaxInterceptable_interceptPreScrollEvents() = runGestureTest {
+ val first = 1f - transitionInterceptionThreshold - tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertThat(progress).isWithin(tolerance).of(first + second)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMoreThanInterceptable_goToIdleOnNextScene() = runGestureTest {
+ val first = 1f - transitionInterceptionThreshold + tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertScene(SceneC, isIdle = true)
+ }
+
@Test
fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
@@ -421,6 +486,7 @@ class SceneGestureHandlerTest {
draggable.onDelta(deltaInPixels10)
assertScene(currentScene = SceneA, isIdle = true)
}
+
@Test
fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest {
draggable.onDragStopped(velocityThreshold)
@@ -437,26 +503,25 @@ class SceneGestureHandlerTest {
@Test
fun startNestedScrollWhileDragging() = runGestureTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = Always)
- draggable.onDragStarted(Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
draggable.onDelta(deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
// now we can intercept the scroll events
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
// this should be ignored, we are scrolling now!
draggable.onDragStopped(velocityThreshold)
assertScene(currentScene = SceneA, isIdle = false)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.3f)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.4f)
+ assertThat(progress).isEqualTo(0.4f)
nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold))
assertScene(currentScene = SceneC, isIdle = false)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
new file mode 100644
index 000000000000..94c51ca50667
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.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.compose.animation.scene
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SceneTransitionLayoutStateTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun isTransitioningTo_idle() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+
+ assertThat(state.isTransitioning()).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
+ .isFalse()
+ }
+
+ @Test
+ fun isTransitioningTo_fromSceneEqualToToScene() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+ state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneA)
+
+ assertThat(state.isTransitioning()).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
+ .isFalse()
+ }
+
+ @Test
+ fun isTransitioningTo_transition() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+ state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB)
+
+ assertThat(state.isTransitioning()).isTrue()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isTrue()
+ assertThat(state.isTransitioning(from = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isTrue()
+ assertThat(state.isTransitioning(to = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
+ }
+
+ private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition {
+ return object : TransitionState.Transition {
+ override val currentScene: SceneKey = from
+ override val fromScene: SceneKey = from
+ override val toScene: SceneKey = to
+ override val progress: Float = 0f
+ override val isInitiatedByUserInput: Boolean = false
+ override val isUserInputOngoing: Boolean = false
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 5afd420a5e16..321cf637824a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -18,6 +18,8 @@ package com.android.compose.animation.scene
import androidx.activity.ComponentActivity
import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -48,6 +50,7 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.subjects.DpOffsetSubject
import com.android.compose.test.subjects.assertThat
import com.google.common.truth.Truth.assertThat
@@ -307,6 +310,26 @@ class SceneTransitionLayoutTest {
assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneA)
}
+ @Test
+ fun layoutSizeIsAnimated() {
+ val layoutTag = "layout"
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(200.dp, 100.dp)) },
+ toSceneContent = { Box(Modifier.size(120.dp, 140.dp)) },
+ transition = {
+ // 4 frames of animation.
+ spec = tween(4 * 16, easing = LinearEasing)
+ },
+ layoutModifier = Modifier.testTag(layoutTag),
+ ) {
+ before { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(200.dp, 100.dp) }
+ at(16) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(180.dp, 110.dp) }
+ at(32) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(160.dp, 120.dp) }
+ at(48) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(140.dp, 130.dp) }
+ after { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(120.dp, 140.dp) }
+ }
+ }
+
private fun SemanticsNodeInteraction.offsetRelativeTo(
other: SemanticsNodeInteraction,
): DpOffset {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
index 2a27763f1d5c..8cffcf6980cc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
@@ -48,7 +48,7 @@ class EdgeTranslateTest {
rule.testTransition(
// The layout under test is 300dp x 300dp.
layoutModifier = Modifier.size(300.dp),
- fromSceneContent = {},
+ fromSceneContent = { Box(Modifier.fillMaxSize()) },
toSceneContent = {
// Foo is 100dp x 100dp in the center of the layout, so at offset = (100dp, 100dp)
Box(Modifier.fillMaxSize()) {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
new file mode 100644
index 000000000000..3ec73c793049
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
@@ -0,0 +1,56 @@
+package com.android.compose.ui.util
+
+import androidx.compose.ui.geometry.Offset
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Scale
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MathHelpersTest {
+
+ @Test
+ fun lerpScaleWithPivotUnspecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f)
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithFirstPivotSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithSecondPivotSpecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f, Offset(1f, 1f))
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithBothPivotsSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f, Offset(3f, 5f))
+ val expectedScale = Scale(3f, 2f, Offset(2f, 3f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index e0ae1be69aaf..06de2965f716 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -104,7 +103,7 @@ fun ComposeContentTestRule.testTransition(
currentScene,
onChangeScene,
transitions { from(fromScene, to = toScene, transition) },
- layoutModifier.fillMaxSize(),
+ layoutModifier,
) {
scene(fromScene, content = fromSceneContent)
scene(toScene, content = toSceneContent)
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
index b4c393e9bfbe..b83705aa64fc 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
@@ -26,6 +26,7 @@ object TestScenes {
val SceneA = SceneKey("SceneA")
val SceneB = SceneKey("SceneB")
val SceneC = SceneKey("SceneC")
+ val SceneD = SceneKey("SceneD")
}
/** Element keys that can be reused by tests. */
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index 5db27d8a9529..bac3553e7498 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -14,7 +14,7 @@ following case-sensitive regex:
The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the
-flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
+flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
Some examples below:
@@ -74,11 +74,11 @@ def main():
#common_typos_disable
flagName = '([a-zA-z0-9_.])+'
- #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
- stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
+ #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
+ stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
#common_typos_enable
- readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|TRUNKFOOD|NEXTFOOD'
+ readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD'
flagRegex = fr'^{field}: .*$'
check_flag = re.compile(flagRegex) #Flag:
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 15d4d2002b2b..67ce86b4e137 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -25,7 +25,7 @@ import com.android.internal.logging.nano.MetricsProto
import com.android.internal.logging.testing.FakeMetricsLogger
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
import com.android.systemui.log.table.TableLogBuffer
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
new file mode 100644
index 000000000000..ce7db80db7da
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.communal.view.viewmodel
+
+import android.app.smartspace.SmartspaceTarget
+import android.provider.Settings
+import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+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
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalEditModeViewModelTest : SysuiTestCase() {
+ @Mock private lateinit var mediaHost: MediaHost
+
+ private lateinit var testScope: TestScope
+
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var tutorialRepository: FakeCommunalTutorialRepository
+ private lateinit var widgetRepository: FakeCommunalWidgetRepository
+ private lateinit var smartspaceRepository: FakeSmartspaceRepository
+ private lateinit var mediaRepository: FakeCommunalMediaRepository
+
+ private lateinit var underTest: CommunalEditModeViewModel
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+
+ val withDeps = CommunalInteractorFactory.create()
+ keyguardRepository = withDeps.keyguardRepository
+ communalRepository = withDeps.communalRepository
+ tutorialRepository = withDeps.tutorialRepository
+ widgetRepository = withDeps.widgetRepository
+ smartspaceRepository = withDeps.smartspaceRepository
+ mediaRepository = withDeps.mediaRepository
+
+ underTest =
+ CommunalEditModeViewModel(
+ withDeps.communalInteractor,
+ mediaHost,
+ )
+ }
+
+ @Test
+ fun communalContent_onlyWidgetsAreShownInEditMode() =
+ testScope.runTest {
+ tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Smartspace available.
+ val target = Mockito.mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+ smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+ // Media playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ // Only Widgets are shown.
+ assertThat(communalContent?.size).isEqualTo(2)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ assertThat(communalContent?.get(1))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ }
+}
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
new file mode 100644
index 000000000000..32f4d075a873
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.communal.view.viewmodel
+
+import android.app.smartspace.SmartspaceTarget
+import android.provider.Settings
+import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+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
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalViewModelTest : SysuiTestCase() {
+ @Mock private lateinit var mediaHost: MediaHost
+
+ private lateinit var testScope: TestScope
+
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var tutorialRepository: FakeCommunalTutorialRepository
+ private lateinit var widgetRepository: FakeCommunalWidgetRepository
+ private lateinit var smartspaceRepository: FakeSmartspaceRepository
+ private lateinit var mediaRepository: FakeCommunalMediaRepository
+
+ private lateinit var underTest: CommunalViewModel
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+
+ val withDeps = CommunalInteractorFactory.create()
+ keyguardRepository = withDeps.keyguardRepository
+ communalRepository = withDeps.communalRepository
+ tutorialRepository = withDeps.tutorialRepository
+ widgetRepository = withDeps.widgetRepository
+ smartspaceRepository = withDeps.smartspaceRepository
+ mediaRepository = withDeps.mediaRepository
+
+ underTest =
+ CommunalViewModel(
+ withDeps.communalInteractor,
+ withDeps.tutorialInteractor,
+ mediaHost,
+ )
+ }
+
+ @Test
+ fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial not started.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(
+ Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+ )
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent!!).isNotEmpty()
+ communalContent!!.forEach { model ->
+ assertThat(model is CommunalContentModel.Tutorial).isTrue()
+ }
+ }
+
+ @Test
+ fun ordering_smartspaceBeforeUmoBeforeWidgets() =
+ testScope.runTest {
+ tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Smartspace available.
+ val target = Mockito.mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+ smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+ // Media playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ // Order is smart space, then UMO, then widget content.
+ assertThat(communalContent?.size).isEqualTo(4)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.Smartspace::class.java)
+ assertThat(communalContent?.get(1)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(communalContent?.get(2))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ assertThat(communalContent?.get(3))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt
new file mode 100644
index 000000000000..61b205710873
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.fold.ui.helper
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FoldPostureTest : SysuiTestCase() {
+
+ @Test
+ fun foldPosture_whenNull_returnsFolded() {
+ assertThat(foldPostureInternal(null)).isEqualTo(FoldPosture.Folded)
+ }
+
+ @Test
+ fun foldPosture_whenHalfOpenHorizontally_returnsTabletop() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.HALF_OPENED,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Tabletop)
+ }
+
+ @Test
+ fun foldPosture_whenHalfOpenVertically_returnsBook() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.HALF_OPENED,
+ orientation = FoldingFeature.Orientation.VERTICAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Book)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndNotSeparating_returnsFullyUnfolded() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ isSeparating = false,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.FullyUnfolded)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndSeparatingHorizontally_returnsTabletop() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ isSeparating = true,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Tabletop)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndSeparatingVertically_returnsBook() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ isSeparating = true,
+ orientation = FoldingFeature.Orientation.VERTICAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Book)
+ }
+
+ private fun createWindowLayoutInfo(
+ state: FoldingFeature.State,
+ orientation: FoldingFeature.Orientation = FoldingFeature.Orientation.VERTICAL,
+ isSeparating: Boolean = false,
+ occlusionType: FoldingFeature.OcclusionType = FoldingFeature.OcclusionType.NONE,
+ ): WindowLayoutInfo {
+ return WindowLayoutInfo(
+ listOf(
+ object : FoldingFeature {
+ override val bounds: Rect = Rect(0, 0, 100, 100)
+ override val isSeparating: Boolean = isSeparating
+ override val occlusionType: FoldingFeature.OcclusionType = occlusionType
+ override val orientation: FoldingFeature.Orientation = orientation
+ override val state: FoldingFeature.State = state
+ }
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
new file mode 100644
index 000000000000..fab290da2953
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.flashlight.domain
+
+import android.graphics.drawable.Drawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FlashlightMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsFlashlightTileConfig
+ private val mapper by lazy { FlashlightMapper(context) }
+
+ @Test
+ fun mapsDisabledDataToInactiveState() {
+ val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
+
+ val actualActivationState = tileState.activationState
+
+ assertEquals(QSTileState.ActivationState.INACTIVE, actualActivationState)
+ }
+
+ @Test
+ fun mapsEnabledDataToActiveState() {
+ val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
+
+ val actualActivationState = tileState.activationState
+ assertEquals(QSTileState.ActivationState.ACTIVE, actualActivationState)
+ }
+
+ @Test
+ fun mapsEnabledDataToOnIconState() {
+ val fakeDrawable = mock<Drawable>()
+ context.orCreateTestableResources.addOverride(
+ R.drawable.qs_flashlight_icon_on,
+ fakeDrawable
+ )
+ val expectedIcon = Icon.Loaded(fakeDrawable, null)
+
+ val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
+
+ val actualIcon = tileState.icon()
+ assertThat(actualIcon).isEqualTo(expectedIcon)
+ }
+
+ @Test
+ fun mapsDisabledDataToOffIconState() {
+ val fakeDrawable = mock<Drawable>()
+ context.orCreateTestableResources.addOverride(
+ R.drawable.qs_flashlight_icon_off,
+ fakeDrawable
+ )
+ val expectedIcon = Icon.Loaded(fakeDrawable, null)
+
+ val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
+
+ val actualIcon = tileState.icon()
+ assertThat(actualIcon).isEqualTo(expectedIcon)
+ }
+
+ @Test
+ fun supportsOnlyClickAction() {
+ val dontCare = true
+ val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(dontCare))
+
+ val supportedActions = tileState.supportedActions
+ assertThat(supportedActions).containsExactly(QSTileState.UserAction.CLICK)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
new file mode 100644
index 000000000000..00572d3163eb
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+import android.os.UserHandle
+import android.testing.LeakCheck
+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.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.utils.leaks.FakeFlashlightController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+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 FlashlightTileDataInteractorTest : SysuiTestCase() {
+ private lateinit var controller: FakeFlashlightController
+ private lateinit var underTest: FlashlightTileDataInteractor
+
+ @Before
+ fun setup() {
+ controller = FakeFlashlightController(LeakCheck())
+ underTest = FlashlightTileDataInteractor(controller)
+ }
+
+ @Test
+ fun availabilityOnMatchesController() = runTest {
+ controller.hasFlashlight = true
+
+ runCurrent()
+ val availability by collectLastValue(underTest.availability(TEST_USER))
+
+ assertThat(availability).isTrue()
+ }
+ @Test
+ fun availabilityOffMatchesController() = runTest {
+ controller.hasFlashlight = false
+
+ runCurrent()
+ val availability by collectLastValue(underTest.availability(TEST_USER))
+
+ assertThat(availability).isFalse()
+ }
+
+ @Test
+ fun dataMatchesController() = runTest {
+ controller.setFlashlight(false)
+ val flowValues: List<FlashlightTileModel> by
+ collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
+
+ runCurrent()
+ controller.setFlashlight(true)
+ runCurrent()
+ controller.setFlashlight(false)
+ runCurrent()
+
+ assertThat(flowValues.size).isEqualTo(3)
+ assertThat(flowValues.map { it.isEnabled }).containsExactly(false, true, false).inOrder()
+ }
+
+ private companion object {
+ val TEST_USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..f819f53838b8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+import android.app.ActivityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.runTest
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FlashlightTileUserActionInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: FlashlightController
+
+ private lateinit var underTest: FlashlightTileUserActionInteractor
+
+ @Before
+ fun setup() {
+ controller = mock<FlashlightController>()
+ underTest = FlashlightTileUserActionInteractor(controller)
+ }
+
+ @Test
+ fun handleClickToEnable() = runTest {
+ assumeFalse(ActivityManager.isUserAMonkey())
+ val stateBeforeClick = false
+
+ underTest.handleInput(click(FlashlightTileModel(stateBeforeClick)))
+
+ verify(controller).setFlashlight(!stateBeforeClick)
+ }
+
+ @Test
+ fun handleClickToDisable() = runTest {
+ assumeFalse(ActivityManager.isUserAMonkey())
+ val stateBeforeClick = true
+
+ underTest.handleInput(click(FlashlightTileModel(stateBeforeClick)))
+
+ verify(controller).setFlashlight(!stateBeforeClick)
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
index 218735229a5d..7b9027004eea 100644
--- a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -22,7 +22,8 @@
android:focusable="true"
android:clickable="true"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:visibility="invisible">
<com.android.systemui.scrim.ScrimView
android:id="@+id/alternate_bouncer_scrim"
diff --git a/packages/SystemUI/res/drawable/ic_assistant_attention_indicator.xml b/packages/SystemUI/res/drawable/ic_assistant_attention_indicator.xml
new file mode 100644
index 000000000000..a1e8b9d0443d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_assistant_attention_indicator.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <group>
+ <path
+ android:pathData="M12 8.13333C12.7089 8.13333 13.2889 8.71333 13.2889 9.42221C13.2889 10.1311 12.7089 10.7111 12 10.7111C11.2911 10.7111 10.7111 10.1311 10.7111 9.42221C10.7111 8.71333 11.2911 8.13333 12 8.13333Z"
+ android:fillColor="#FFFFFF"
+ />
+ <path
+ android:pathData="M12 13.9333C13.74 13.9333 15.7378 14.7647 15.8667 15.2222V15.8667H8.13333V15.2287C8.26221 14.7647 10.26 13.9333 12 13.9333Z"
+ android:fillColor="#FFFFFF"
+ />
+ <path
+ android:pathData="M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24ZM9.42228 9.42224C9.42228 7.99802 10.5758 6.84447 12.0001 6.84447C13.4243 6.84447 14.5778 7.99802 14.5778 9.42224C14.5778 10.8465 13.4243 12 12.0001 12C10.5758 12 9.42228 10.8465 9.42228 9.42224ZM12 12.6445C10.2794 12.6445 6.84447 13.508 6.84447 15.2223V17.1556H17.1556V15.2223C17.1556 13.508 13.7207 12.6445 12 12.6445Z"
+ android:fillColor="#FFFFFF"
+ android:fillType="evenOdd"
+ />
+ </group>
+</vector>
+
+
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
index be8fe8c57215..ff1146e9b945 100644
--- a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
@@ -19,9 +19,14 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
- <path android:fillColor="@color/ksh_key_item_color"
- android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
-3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
-19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
-14z" />
+ <path android:pathData="M5.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M5.5,16.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M16.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M18.5,16.5C18.5,15.4 17.6,14.5 16.5,14.5C15.4,14.5 14.5,15.4 14.5,16.5C14.5,17.6 15.4,18.5 16.5,18.5C17.6,18.5 18.5,17.6 18.5,16.5ZM12.5,16.5C12.5,14.29 14.29,12.5 16.5,12.5C18.71,12.5 20.5,14.29 20.5,16.5C20.5,17.241 20.299,17.934 19.948,18.529L23,21.59L21.59,23L18.529,19.948C17.934,20.299 17.241,20.5 16.5,20.5C14.29,20.5 12.5,18.71 12.5,16.5Z"
+ android:fillColor="@color/ksh_key_item_color"
+ android:fillType="evenOdd" />
</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_person_outline.xml b/packages/SystemUI/res/drawable/ic_person_outline.xml
deleted file mode 100644
index d94714e0d51a..000000000000
--- a/packages/SystemUI/res/drawable/ic_person_outline.xml
+++ /dev/null
@@ -1,26 +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.
- -->
-
-<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/black"
- android:pathData="M480,480Q414,480 367,433Q320,386 320,320Q320,254 367,207Q414,160 480,160Q546,160 593,207Q640,254 640,320Q640,386 593,433Q546,480 480,480ZM160,800L160,688Q160,654 177.5,625.5Q195,597 224,582Q286,551 350,535.5Q414,520 480,520Q546,520 610,535.5Q674,551 736,582Q765,597 782.5,625.5Q800,654 800,688L800,800L160,800ZM240,720L720,720L720,688Q720,677 714.5,668Q709,659 700,654Q646,627 591,613.5Q536,600 480,600Q424,600 369,613.5Q314,627 260,654Q251,659 245.5,668Q240,677 240,688L240,720ZM480,400Q513,400 536.5,376.5Q560,353 560,320Q560,287 536.5,263.5Q513,240 480,240Q447,240 423.5,263.5Q400,287 400,320Q400,353 423.5,376.5Q447,400 480,400ZM480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320ZM480,720L480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720L480,720L480,720Z"/>
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml b/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml
index 34e7d0afcd97..f7c04b5366f8 100644
--- a/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml
+++ b/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml
@@ -26,10 +26,17 @@
</shape>
</item>
<item
- android:drawable="@drawable/ic_ksh_key_down"
- android:gravity="end|center_vertical"
- android:textColor="?androidprv:attr/textColorPrimary"
- android:width="@dimen/screenrecord_spinner_arrow_size"
- android:height="@dimen/screenrecord_spinner_arrow_size"
- android:end="20dp" />
+ android:end="20dp"
+ android:gravity="end|center_vertical">
+ <vector
+ android:width="@dimen/screenrecord_spinner_arrow_size"
+ android:height="@dimen/screenrecord_spinner_arrow_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?androidprv:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
+ </vector>
+ </item>
</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/shortcut_button_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
index a21489cc47aa..bf908532ac17 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
@@ -22,8 +22,8 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp"/>
- <solid android:color="?androidprv:attr/colorSurface"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
</shape>
</item>
</ripple>
-</inset> \ No newline at end of file
+</inset>
diff --git a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
index 2ff32b6ee9b5..f692ed975319 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
@@ -22,8 +22,8 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp"/>
- <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/materialColorPrimary"/>
</shape>
</item>
</ripple>
-</inset> \ No newline at end of file
+</inset>
diff --git a/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
new file mode 100644
index 000000000000..6c4d4fbcc1fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<ripple android:color="?android:attr/colorControlHighlight" android:radius="24dp">
+ <item>
+ <shape android:shape="oval">
+ <size android:width="24dp"
+ android:height="24dp" />
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ </shape>
+ </item>
+</ripple>
+</inset>
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index 2459eeaa9b19..cb638ee87834 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -28,26 +28,28 @@
style="@android:style/Widget.DeviceDefault.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:text="@string/app_clips_save_add_to_note"
android:layout_marginStart="8dp"
android:background="@drawable/overlay_button_background"
+ android:paddingHorizontal="24dp"
+ android:text="@string/app_clips_save_add_to_note"
android:textColor="?android:textColorSecondary"
+ app:layout_constraintBottom_toTopOf="@id/preview"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/preview" />
+ app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/cancel"
style="@android:style/Widget.DeviceDefault.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:text="@android:string/cancel"
- android:layout_marginStart="6dp"
+ android:layout_marginStart="8dp"
android:background="@drawable/overlay_button_background"
+ android:paddingHorizontal="24dp"
+ android:text="@android:string/cancel"
android:textColor="?android:textColorSecondary"
+ app:layout_constraintBottom_toTopOf="@id/preview"
app:layout_constraintStart_toEndOf="@id/save"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/preview" />
+ app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/preview"
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 08eccbbe9669..4336ccc70c33 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -21,7 +21,7 @@
style="@style/BluetoothTileDialog.Device"
android:layout_width="match_parent"
android:layout_height="@dimen/bluetooth_dialog_device_height"
- android:paddingEnd="24dp"
+ android:paddingEnd="0dp"
android:paddingStart="20dp"
android:layout_marginBottom="4dp">
@@ -86,6 +86,7 @@
android:id="@+id/gear_icon_image"
android:layout_width="0dp"
android:layout_height="24dp"
+ android:paddingEnd="24dp"
android:src="@drawable/ic_settings_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index c11a18b795a1..af29cada2657 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -120,6 +120,16 @@
android:visibility="gone"
app:constraint_referenced_ids="ic_arrow,see_all_text" />
+ <View
+ android:id="@+id/see_all_clickable_row"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/device_list"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" />
+
<ImageView
android:id="@+id/ic_arrow"
android:layout_marginStart="36dp"
@@ -141,6 +151,8 @@
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:clickable="false"
android:layout_marginStart="0dp"
android:paddingStart="20dp"
android:text="@string/see_all_bluetooth_devices"
@@ -158,6 +170,16 @@
android:visibility="gone"
app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+ <View
+ android:id="@+id/pair_new_device_clickable_row"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_text"
+ app:layout_constraintBottom_toTopOf="@+id/done_button" />
+
<ImageView
android:id="@+id/ic_add"
android:layout_width="24dp"
@@ -180,6 +202,8 @@
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:clickable="false"
android:layout_marginStart="0dp"
android:paddingStart="20dp"
android:text="@string/pair_new_bluetooth_devices"
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index ad9a775ed243..ec2edb52a039 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -113,7 +113,7 @@
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
- android:src="@drawable/dream_overlay_assistant_attention_indicator"
+ android:src="@drawable/ic_assistant_attention_indicator"
android:visibility="gone"
android:contentDescription="@string/assistant_attention_content_description" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index 13425c9259de..dbcd2636134f 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -53,7 +53,7 @@
android:hint="@string/keyboard_shortcut_search_list_hint"
android:textColorHint="?android:attr/textColorTertiary" />
- <ImageView
+ <ImageButton
android:id="@+id/keyboard_shortcuts_search_cancel"
android:layout_gravity="center_vertical|end"
android:layout_width="wrap_content"
@@ -61,7 +61,10 @@
android:layout_marginEnd="49dp"
android:padding="16dp"
android:contentDescription="@string/keyboard_shortcut_clear_text"
- android:src="@drawable/ic_shortcutlist_search_button_cancel" />
+ android:src="@drawable/ic_shortcutlist_search_button_cancel"
+ android:background="@drawable/shortcut_search_cancel_button"
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:pointerIcon="arrow" />
</FrameLayout>
<HorizontalScrollView
@@ -82,15 +85,13 @@
android:id="@+id/shortcut_system"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
style="@style/ShortCutButton"
- android:text="@string/keyboard_shortcut_search_category_system"/>
+ android:text="@string/keyboard_shortcut_search_category_system" />
<Button
android:id="@+id/shortcut_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_input"/>
@@ -98,7 +99,6 @@
android:id="@+id/shortcut_open_apps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_open_apps"/>
@@ -106,7 +106,6 @@
android:id="@+id/shortcut_specific_app"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_current_app"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index b8f4c0f212c3..7ab44e70e6fe 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -53,6 +53,8 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
+ android:focusable="true"
+ android:importantForAccessibility="no"
android:tint="?attr/shadeActive"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 7e03bd9d4325..ca0fb85beb6e 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -30,7 +30,6 @@
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
@@ -38,7 +37,6 @@
android:id="@+id/scrim_notifications"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
@@ -78,7 +76,6 @@
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
@@ -119,11 +116,6 @@
android:inflatedId="@+id/multi_shade"
android:layout="@layout/multi_shade" />
- <include layout="@layout/alternate_bouncer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml b/packages/SystemUI/res/layout/udfps_touch_overlay.xml
index bc1775ee64ae..ea92776aba2d 100644
--- a/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml
+++ b/packages/SystemUI/res/layout/udfps_touch_overlay.xml
@@ -14,22 +14,9 @@
~ 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="@id/background"
- android:gravity="center">
- <shape android:shape="oval">
- <size
- android:height="24px"
- android:width="24px"
- />
- <solid android:color="#FFFFFFFF" />
- </shape>
- </item>
- <item android:id="@id/icon"
- android:gravity="center"
- android:width="20px"
- android:height="20px"
- android:drawable="@drawable/ic_person_outline"
- />
-</layer-list> \ No newline at end of file
+<com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_touch_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/accessibility_fingerprint_label">
+</com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay>
diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml
new file mode 100644
index 000000000000..827bd5d2e0b1
--- /dev/null
+++ b/packages/SystemUI/res/layout/widget_picker.xml
@@ -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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widgets_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="64dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index cb061ed1f23d..b0c4fa56a73a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth gekoppel."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth-toestelikoon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klik om toestelbesonderhede op te stel."</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterypersentasie is onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Gekoppel aan <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Gekoppel aan <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans vinnig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klik die pylknoppie om die gemeenskaplike tutoriaal te begin"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swiep links om die gemeenskaplike tutoriaal te begin"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Onlangs gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Word gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Onlangs gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 73f446cc51bb..b17455241d57 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"የባትሪ መቶኛ አይታወቅም።"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ከ<xliff:g id="BLUETOOTH">%s</xliff:g> ጋር ተገናኝቷል።"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"ከ<xliff:g id="CAST">%s</xliff:g> ጋር ተገናኝቷል።"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"የጋራ አጋዥ ሥልጠናን ለመጀመር የቀስት አዝራሩ ላይ ጠቅ ያድርጉ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"የጋራ አጋዥ ሥልጠናውን ለመጀመር ወደ ግራ ያንሸራትቱ።"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ምግብር መራጩን ክፈት"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ምግብርን አስወግድ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"በቅርብ ጊዜ በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ጥቅም ላይ ውሏል"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ጥቅም ላይ እየዋለ ነው"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"በቅርብ ጊዜ በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ጥቅም ላይ ውሏል"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index cc2f48852b23..0b53b41ffa7d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"نسبة شحن البطارية غير معروفة."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"متصل بـ <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"تم الاتصال بـ <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن سريعًا • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن ببطء • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"انقر على زر السهم لبدء الدليل التوجيهي العام."</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"مرِّر سريعًا لليمين لبدء الدليل التوجيهي العام."</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"تم الاستخدام مؤخرًا في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"قيد الاستخدام في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"تم الاستخدام مؤخرًا في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 4e7599551028..e593a5bb731b 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"বেটাৰীৰ চাৰ্জৰ শতাংশ অজ্ঞাত।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>ত সংযোগ হ’ল।"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্ৰুতগতিৰে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • লাহে লাহে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"সম্প্ৰদায় সম্পৰ্কীয় নিৰ্দেশনা আৰম্ভ কৰিবলৈ কাঁড়চিহ্নৰ বুটামটোত ক্লিক কৰক"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"সম্প্ৰদায় সম্পৰ্কীয় নিৰ্দেশনা আৰম্ভ কৰিবলৈ বাওঁফালে ছোৱাইপ কৰক"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ৱিজেট বাছনিকৰ্তাটো খোলক"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"এটা ৱিজেট আঁতৰাওক"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"শেহতীয়াকৈ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)এ ব্যৱহাৰ কৰিছে"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)এ ব্যৱহাৰ কৰি আছে"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"শেহতীয়াকৈ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)এ ব্যৱহাৰ কৰিছে"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 3e6765da3d60..4a2a595c8893 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth qoşulub."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth cihazı ikonası"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Cihaz təfərrüatlarını konfiqurasiya etmək üçün klikləyin"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareyanın faizi naməlumdur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoşuldu."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> cihazına qoşulub."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth aç"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sürətlə şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Asta şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"İcma təlimatını başlatmaq üçün ox düyməsinə klikləyin"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"İcma təlimatını başlatmaq üçün sola sürüşdürün"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Bu yaxınlarda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) istifadə edib"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) istifadə edir"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Bu yaxınlarda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) istifadə edib"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 17e9f9bf3ebb..c7f2eafb9a4f 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je priključen."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona Bluetooth uređaja"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknite da biste konfigurisali detalje o uređaju"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procenat napunjenosti baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezani ste sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Povezani smo sa uređajem <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknite na dugme sa strelicom da biste započeli zajednički vodič"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulevo da biste započeli zajednički vodič"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Otvori birač vidžeta"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Ukloni vidžet"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koriste <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 252b2bd70bb4..41d674416f86 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-сувязь."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Значок прылады з Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Націсніце, каб задаць падрабязныя налады прылады"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Працэнт зараду акумулятара невядомы."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Падлучаны да <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ёсць падключэнне да <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе хуткая зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе павольная зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Націсніце кнопку са стрэлкай, каб азнаёміцца з дапаможнікам"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Правядзіце пальцам па экране ўлева, каб азнаёміцца з дапаможнікам"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Зараз выкарыстоўваецца праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 4beb901c6051..8f64831df295 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е включен."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Икона за устройство с Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Кликнете, за да конфигурирате подробностите за устройството"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентът на батерията е неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Има връзка с <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Установена е връзка с/ъс <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бързо • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бавно • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Кликнете върху бутона със стрелка, за да стартирате общия урок"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Прекарайте пръст наляво, за да стартирате общия урок"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Наскоро използвано от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Използва се от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Наскоро използвано от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index cd15261c241e..809a25d583bf 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ব্যাটারি কত শতাংশ আছে তা জানা যায়নি।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>এ সংযুক্ত হয়ে আছে।"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> এর সাথে সংযুক্ত৷"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্রুত চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জ হচ্ছে • পুরো চার্জ হতে আরও <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> সময় লাগবে"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"কমিউনিটি টিউটোরিয়াল চালু করতে তীরচিহ্ন বোতামে ক্লিক করুন"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"কমিউনিটি টিউটোরিয়াল চালু করতে বাঁদিকে সোয়াইপ করুন"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"সম্প্রতি <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ব্যবহার করা হয়েছে"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ব্যবহার করা হচ্ছে"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"সম্প্রতি <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ব্যবহার করা হয়েছে"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a6d1f2b7c975..f97b05569e57 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je povezan."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona Bluetooth uređaja"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknite da konfigurirate detalje uređaja"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak napunjenosti baterije nije poznat"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezan na <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Povezan na <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknite na dugme sa strelicom da pokrenete zajednički vodič"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulijevo da pokrenete zajednički vodič"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Otvaranje birača vidžeta"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Uklanjanje vidžeta"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -438,7 +448,7 @@
<string name="media_projection_task_switcher_notification_channel" msgid="7613206306777814253">"Promjena aplikacije"</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blokirao je vaš IT administrator"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Snimanje ekrana je onemogućeno pravilima uređaja"</string>
- <string name="clear_all_notifications_text" msgid="348312370303046130">"Očisti sve"</string>
+ <string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historija"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 143bd4356ffd..ba82dfe47298 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connectat."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icona de dispositiu Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Fes clic per configurar els detalls del dispositiu"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Es desconeix el percentatge de bateria."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"S\'ha connectat a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Està connectat amb <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza\'l"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant ràpidament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Fes clic al botó de la fletxa per iniciar el tutorial de la comunitat"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Llisca cap a l\'esquerra per iniciar el tutorial de la comunitat"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Utilitzat recentment per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En ús per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Utilitzat recentment per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 9ffc4b219039..2201d64676b9 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Rozhraní Bluetooth je připojeno."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona zařízení Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknutím nakonfigurujete podrobnosti o zařízení"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procento baterie není známé."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Připojeno k zařízení <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Jste připojeni k zařízení <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Použít Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rychlé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknutím na tlačítko s šipkou spustíte společný výukový program"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Přejetím doleva spustíte komunitní výukový program"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Otevřít výběr widgetu"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Odstranit widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno použila aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Právě používán aplikací <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno použila aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0eb508481958..14bce296477e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tilsluttet."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikon for Bluetooth-enhed"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klik for at konfigurere enhedsoplysninger"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriniveauet er ukendt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tilsluttet <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Forbundet til <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader hurtigt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klik på pilen for at starte den fælles vejledning"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Stryg mod venstre for at starte den fælles vejledning"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -503,7 +515,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="csd_lowered_title" product="default" msgid="2464112924151691129">"Lydstyrken blev sænket til et mere sikkert niveau"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Fortsæt med at lytte"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Brugt for nylig af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Bruges af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Brugt for nylig af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index c9050f505da8..2d9a83023018 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Mit Bluetooth verbunden"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Symbol des Bluetooth-Geräts"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klicke, um das Gerätedetail zu konfigurieren"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akkustand unbekannt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Mit <xliff:g id="BLUETOOTH">%s</xliff:g> verbunden"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Verbunden mit <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -249,13 +253,16 @@
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bitte nicht stören"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Keine gekoppelten Geräte verfügbar"</string>
- <!-- no translation found for quick_settings_bluetooth_tile_subtitle (212752719010829550) -->
- <skip />
+ <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Zum Verbinden oder Trennen eines Geräts tippen"</string>
<string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Neues Gerät koppeln"</string>
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle ansehen"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -397,7 +404,10 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird schnell geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird langsam geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klicke auf die Pfeilschaltfläche, um das Community-Tutorial zu starten"</string>
+ <!-- no translation found for communal_tutorial_indicator_text (4503010353591430123) -->
+ <skip />
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Widget-Auswahl öffnen"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Widget entfernen"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -504,7 +514,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="csd_lowered_title" product="default" msgid="2464112924151691129">"Auf sicherere Lautstärke gesenkt"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Weiterhören"</string>
@@ -1206,4 +1216,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Verwendet von <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 80eef9f3690c..72fe0804889f 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Το Bluetooth είναι συνδεδεμένο."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Εικονίδιο συσκευής Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Κάντε κλικ για να διαμορφώσετε τις λεπτομέρειες συσκευής"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Άγνωστο ποσοστό μπαταρίας."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Συνδέθηκε στο <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Συνδέθηκε σε <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Γρήγορη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Κάντε κλικ στο κουμπί βέλους για να ξεκινήσετε τον κοινόχρηστο οδηγό"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Σύρετε προς τα αριστερά για να ξεκινήσετε τον κοινόχρηστο οδηγό"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Άνοιγμα του εργαλείου επιλογής γραφικών στοιχείων"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Αφαίρεση ενός widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Χρησιμοποιήθηκε πρόσφατα από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Χρησιμοποιείται από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Χρησιμοποιήθηκε πρόσφατα από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index c6fe16906954..d7062e157c4a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth device icon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Click to configure device detail"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging rapidly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Click on the arrow button to start the communal tutorial"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Open the widget picker"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remove a widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f9aa1cbf141a..9fcf1a3fc68e 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth device icon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Click to configure device detail"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging rapidly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Click on the arrow button to start the communal tutorial"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Open the widget picker"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remove a widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index c6fe16906954..d7062e157c4a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth device icon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Click to configure device detail"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging rapidly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Click on the arrow button to start the communal tutorial"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Open the widget picker"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remove a widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index c6fe16906954..d7062e157c4a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth device icon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Click to configure device detail"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging rapidly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Click on the arrow button to start the communal tutorial"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Open the widget picker"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remove a widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 23f7483ac362..a68c7c557fcf 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎Bluetooth connected.‎‏‎‎‏‎"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‎Bluetooth device icon‎‏‎‎‏‎"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎Click to configure device detail‎‏‎‎‏‎"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎Battery percentage unknown.‎‏‎‎‏‎"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎Connected to ‎‏‎‎‏‏‎<xliff:g id="BLUETOOTH">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎Connected to ‎‏‎‎‏‏‎<xliff:g id="CAST">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎Use Bluetooth‎‏‎‎‏‎"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎Connected‎‏‎‎‏‎"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎Saved‎‏‎‎‏‎"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • Charging rapidly • Full in ‎‏‎‎‏‏‎<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • Charging slowly • Full in ‎‏‎‎‏‏‎<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • Charging • Full in ‎‏‎‎‏‏‎<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎Click on the arrow button to start the communal tutorial‎‏‎‎‏‎"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎Swipe left to start the communal tutorial‎‏‎‎‏‎"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‎Open the widget picker‎‏‎‎‏‎"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎Remove a widget‎‏‎‎‏‎"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎Switch user‎‏‎‎‏‎"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎pulldown menu‎‏‎‎‏‎"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎All apps and data in this session will be deleted.‎‏‎‎‏‎"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎Recently used by ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎In use by ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • ‎‏‎‎‏‏‎<xliff:g id="PROXY_LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎Recently used by ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • ‎‏‎‎‏‏‎<xliff:g id="PROXY_LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎Keyboard backlight‎‏‎‎‏‎"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎Level %1$d of %2$d‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7c3960e49f69..03acc6a43cc2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ícono de dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Haz clic para configurar los detalles del dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Se desconoce el porcentaje de la batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Haz clic en el botón de flecha para iniciar el instructivo comunal"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza el dedo a la izquierda para iniciar el instructivo comunal"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Abre el selector de widgets"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Quita el widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Uso reciente en <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Uso reciente en <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index f65b2e520f99..18131372ec40 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icono de dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Haz clic para configurar la información del dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentaje de batería desconocido."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Carga completa en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Haz clic en el botón de la flecha para iniciar el tutorial de la comunidad"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d648f87abd51..57dbb96c7143 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth on ühendatud."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth-seadme ikoon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klõpsake seadme üksikasjade konfigureerimiseks"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Aku laetuse protsent on teadmata."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ühendatud: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ühendatud ülekandega <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kiirlaadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Aeglane laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Ühise õpetuse käivitamiseks klõpsake noolenuppu"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ühise õpetuse käivitamiseks pühkige vasakule"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kasutas hiljuti rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Seda kasutab <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kasutas hiljuti rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index e3c32e96bf42..0902354bfa82 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetootha konektatuta."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth bidezko gailuaren ikonoa"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Gailuaren xehetasuna konfiguratzeko, sakatu hau"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Bateriaren ehunekoa ezezaguna da."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> gailura konektatuta."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Hona konektatuta: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bizkor kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Tutorial komuna hasteko, sakatu geziaren botoia"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Tutorial komuna hasteko, pasatu hatza ezkerrera"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -659,7 +671,7 @@
<string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Kendu eranskina"</string>
<string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Hasierako pantaila"</string>
- <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Azkenak"</string>
+ <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Azkenaldikoak"</string>
<string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Atzera"</string>
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Lasterbideak"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) aplikazioak erabili du duela gutxi"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak darabil (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) aplikazioak erabili du duela gutxi"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 805feffc43cd..f42bf5038298 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"درصد شارژ باتری مشخص نیست."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"به <xliff:g id="CAST">%s</xliff:g> متصل شد."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن سریع • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ شدن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"برای شروع آموزش گام‌به‌گام عمومی، روی دکمه جهت‌نما کلیک کنید"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"برای شروع آموزش گام‌به‌گام عمومی، تند به‌چپ بکشید"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"باز کردن انتخابگر ابزارک"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"حذف ابزارک"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده می‌کند (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9a8cecacd5aa..ae3ac060d494 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth yhdistetty."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth-laitekuvake"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Määritä laitteen asetukset klikkaamalla"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akun varaustaso ei tiedossa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Yhteys: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Yhdistetty kohteeseen <xliff:g id="CAST">%s</xliff:g>"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu nopeasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu hitaasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Aloita yhteisöesittely klikkaamalla nuolipainiketta"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aloita yhteisöesittely pyyhkäisemällä vasemmalle"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -504,7 +516,7 @@
<string name="sound_settings" msgid="8874581353127418308">"Ääni ja värinä"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Asetukset"</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 yli suositellun ajan"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Jatka kuuntelua"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Vähennä äänenvoimakkuutta"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Tämän käytössä: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 79f0c44ace4e..286753d01041 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icône de l\'appareil Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Cliquez pour configurer les détails de l\'appareil"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la pile inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"En recharge rapide : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"En recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge en cours… • Se terminera dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Cliquez sur le bouton de flèche pour démarrer le tutoriel communautaire"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer l\'écran vers la gauche pour démarrer le tutoriel communautaire"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4988cf568459..7f4e4cc36d5c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icône de l\'appareil Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Cliquer pour configurer les détails de l\'appareil"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la batterie inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge rapide • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Cliquer sur la flèche pour démarrer le tutoriel collectif"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer vers la gauche pour démarrer le tutoriel collectif"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Ouvrez le sélecteur de widgets"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Retirez un widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En cours d\'utilisation par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 90f1535314b3..88d0a314789d 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icona do dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Facer clic para configurar os detalles do dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Descoñécese a porcentaxe da batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Dispositivo conectado: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Fai clic no botón da frecha para iniciar o titorial comunitario"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pasa o dedo cara á esquerda para iniciar o titorial comunitario"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En uso recentemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En uso recentemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 302dab11b1c6..92638a22fc79 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"બૅટરીની ટકાવારી અજાણ છે."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> થી કનેક્ટ કરેલ."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ઝડપથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ધીમેથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં પૂરું ચાર્જ થઈ જશે"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"કૉમ્યુનલ ટ્યૂટૉરિઅલ શરૂ કરવા માટે ઍરો બટન પર ક્લિક કરો"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"કૉમ્યુનલ ટ્યૂટૉરિઅલ શરૂ કરવા માટે ડાબે સ્વાઇપ કરો"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"વિજેટ પિકર ખોલો"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"કોઈ વિજેટ કાઢી નાખો"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -719,7 +729,7 @@
<string name="switch_bar_off" msgid="5669805115416379556">"બંધ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ઉપલબ્ધ નથી"</string>
<string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"વધુ જાણો"</string>
- <string name="nav_bar" msgid="4642708685386136807">"નેવિગેશન બાર"</string>
+ <string name="nav_bar" msgid="4642708685386136807">"નૅવિગેશન બાર"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"લેઆઉટ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"અતિરિક્ત ડાબો બટન પ્રકાર"</string>
<string name="right_nav_bar_button_type" msgid="4472566498647364715">"અતિરિક્ત જમણો બટન પ્રકાર"</string>
@@ -738,7 +748,7 @@
<string name="save" msgid="3392754183673848006">"સાચવો"</string>
<string name="reset" msgid="8715144064608810383">"રીસેટ કરો"</string>
<string name="clipboard" msgid="8517342737534284617">"ક્લિપબોર્ડ"</string>
- <string name="accessibility_key" msgid="3471162841552818281">"કસ્ટમ નેવિગેશન બટન"</string>
+ <string name="accessibility_key" msgid="3471162841552818281">"કસ્ટમ નૅવિગેશન બટન"</string>
<string name="left_keycode" msgid="8211040899126637342">"ડાબો કીકોડ"</string>
<string name="right_keycode" msgid="2480715509844798438">"જમણો કીકોડ"</string>
<string name="left_icon" msgid="5036278531966897006">"ડાબું આઇકન"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) દ્વારા ઉપયોગ ચાલુ છે"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 5df950cbdc19..213457fef803 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"इस बारे में जानकारी नहीं है कि अभी बैटरी कितने प्रतिशत चार्ज है."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> से कनेक्ट किया गया."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> से कनेक्ट है."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • तेज़ चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"कम्यूनिटी ट्यूटोरियल शुरू करने के लिए, तीर के निशान वाले बटन पर क्लिक करें"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्यूनिटी ट्यूटोरियल शुरू करने के लिए, बाईं ओर स्वाइप करें"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"विजेट पिकर को खोलें"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"विजेट को हटाएं"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"आवाज़ और वाइब्रेशन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग"</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_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय से देर तक ज़्यादा रही"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"इस हफ़्ते के लिए हेडफ़ोन की आवाज़, सुझाई गई सीमा से ज़्यादा हो गई है"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"सुनना जारी रखें"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"आवाज़ कम करें"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने इस्तेमाल किया"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) पर इस्तेमाल किया जा रहा है"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ने इस्तेमाल किया"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index de60e8e00a0d..cf86c466b645 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth povezan."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona Bluetooth uređaja"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknite da biste konfigurirali pojedinosti o uređaju"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Spojen na <xliff:g id="BLUETOOTH">%s</xliff:g> ."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Povezani ste sa sljedećim uređajem: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Uključi"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • brzo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknite gumb sa strelicom da biste pokrenuli zajednički vodič"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prijeđite prstom ulijevo da biste pokrenuli zajednički vodič"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Otvaranje alata za odabir widgeta"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Uklanjanje widgeta"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"Zvuk i vibracija"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Postavke"</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">"Pojačana je glasnoća u slušalicama dulje nego što se preporučuje"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Nastavi slušati"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Stišaj"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 138098530188..6f1d2c609bb0 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth csatlakoztatva."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth-eszköz ikon"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kattintson az eszköz beállításainak megadásához"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Az akkumulátor töltöttségi szintje ismeretlen."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Csatlakoztatva a következőhöz: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Csatlakozva a következőhöz: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Gyors töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kattintson a nyíl gombra a közösségi útmutató elindításához"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Csúsztasson gyorsan balra a közösségi útmutató elindításához"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"A modulválasztó megnyitása"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"A modul eltávolítása"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Legutóbb használta: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Használatban a következő által: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Legutóbb használta: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index c31a16284cb4..8ceeb4b187bb 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-ը միացված է:"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth սարքի պատկերակ"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Սեղմեք՝ սարքի մանրամասները կազմաձևելու համար"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Մարտկոցի լիցքի մակարդակն անհայտ է։"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Միացված է <xliff:g id="BLUETOOTH">%s</xliff:g>-ին:"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Միացված է <xliff:g id="CAST">%s</xliff:g>-ին:"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Արագ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Դանդաղ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Սեղմեք սլաքի կոճակը՝ ուղեցույցը գործարկելու համար"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Թերթեք ձախ՝ ուղեցույցը գործարկելու համար"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 56d038181ccc..285bb3935b33 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth terhubung."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikon perangkat Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klik untuk mengonfigurasi detail perangkat"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Persentase baterai tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Terhubung ke <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Terhubung ke <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan cepat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan lambat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klik tombol panah untuk memulai tutorial komunal"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Geser ke kiri untuk memulai tutorial komunal"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -504,7 +516,7 @@
<string name="sound_settings" msgid="8874581353127418308">"Suara &amp; getaran"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setelan"</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 selama lebih lama dari yang direkomendasikan"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Terus dengarkan"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Turunkan volume"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Baru saja digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Sedang digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Baru saja digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1e517b1192f5..2cf9237766ac 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tengt."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Tákn Bluetooth-tækis"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Smelltu til að stilla tækjaupplýsingar"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Staða rafhlöðu óþekkt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tengt við <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Tengt við <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hraðhleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Smelltu á örvahnappinn til að hefja samfélagsleiðsögnina"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Strjúktu til vinstri til að hefja samfélagsleiðsögnina"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nýlega notað af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Í notkun í <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nýlega notað af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 03faea64cce0..4912a53f8c6d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth collegato."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icona del dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Fai clic per configurare i dettagli del dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentuale della batteria sconosciuta."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connesso a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Connesso a: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica veloce • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Fai clic sul pulsante Freccia per iniziare il tutorial della community"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Scorri a sinistra per iniziare il tutorial della community"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recentemente in uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recentemente in uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1d8eadc7077b..eef614225afe 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -196,7 +196,10 @@
<string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"אי אפשר לפתוח בזיהוי פנים"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"‏Bluetooth מחובר."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"‏סמל של מכשיר Bluetooth"</string>
- <!-- no translation found for accessibility_bluetooth_device_settings_gear (3314916468105272540) -->
+ <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"יש ללחוץ כדי להגדיר את פרטי המכשיר"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
<skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"אחוז טעינת הסוללה לא ידוע."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"התבצע חיבור אל <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -256,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -397,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה מהירה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה איטית • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"אפשר ללחוץ על לחצן החץ כדי להפעיל את המדריך המשותף"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"אפשר להחליק שמאלה כדי להפעיל את המדריך המשותף"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -1181,8 +1192,7 @@
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"המצלמה והמיקרופון חסומים"</string>
<string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"המיקרופון חסום"</string>
<string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"מצב \'עדיפות\' מופעל"</string>
- <!-- no translation found for assistant_attention_content_description (4166330881435263596) -->
- <skip />
+ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string>
@@ -1207,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"בשימוש על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f0076588d9b7..eaf719a49f36 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetoothに接続済み。"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth デバイスのアイコン"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"クリックしてデバイスの詳細を設定します"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"バッテリー残量は不明です。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>に接続しました。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>に接続されています。"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存しました"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 急速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • フル充電まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"矢印ボタンをクリックすると、コミュニティ チュートリアルが開始します"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"左にスワイプすると、コミュニティ チュートリアルが開始します"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ウィジェット選択ツールを開きます"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ウィジェットを削除します"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"音とバイブレーション"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</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_system_lowered_text" product="default" msgid="1250251883692996888">"推奨時間よりも長くヘッドフォンが大音量で設定されています"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ヘッドフォンの音量が今週一週間の安全基準とされる音量、時間を超えています"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"引き続き聴く"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"音量を下げる"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> が最近使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> が使用中(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> が最近使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 562962af1f6b..b3295e3220f9 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth დაკავშირებულია."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth მოწყობილობის ხატულა"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"დააწკაპუნეთ მოწყობილობის დეტალების კონფიგურირებისთვის"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ბატარეის პროცენტული მაჩვენებელი უცნობია."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"დაკავშირებულია <xliff:g id="BLUETOOTH">%s</xliff:g>-თან."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"დაკავშირებულია მოწყობილობასთან: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • სწრაფად იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ნელა იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"დააწკაპუნეთ ისრის ღილაკზე, რათა დაიწყოთ საერთო სახელმძღვანელო"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"გადაფურცლეთ მარცხნივ, რათა დაიწყოთ საერთო სახელმძღვანელო"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ახლახან გამოყენებულია <xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"გამოიყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ახლახან გამოყენებულია <xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 7532a6414aa5..3dc2ebcee7b6 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth қосылған."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth құрылғысы белгішесі"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Құрылғы деректерін конфигурациялау үшін басыңыз."</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея зарядының мөлшері белгісіз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> трансляциясына қосылды."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жылдам зарядтау • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Баяу зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядталып жатыр. • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Жалпы оқулықты ашу үшін бағыт түймесін басыңыз."</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ортақ оқулықты ашу үшін солға қарай сырғытыңыз."</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -503,7 +515,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="csd_lowered_title" product="default" msgid="2464112924151691129">"Дыбыс деңгейі қауіпсіз шекке дейін түсірілді"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Тыңдай беру"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Соңғы рет <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) қолданбасы пайдаланды."</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) қолданбасы пайдаланып жатыр"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Соңғы рет <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) қолданбасы пайдаланды."</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index c8236355fdd3..6ba41b83d4ed 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"មិនដឹងអំពី​ភាគរយថ្មទេ។"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"បាន​ភ្ជាប់​ទៅ <xliff:g id="BLUETOOTH">%s</xliff:g> ។"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"បានភ្ជាប់ទៅ <xliff:g id="CAST">%s</xliff:g>"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្ម​យឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"ចុចលើប៊ូតុងសញ្ញាព្រួញ ដើម្បីចាប់ផ្ដើមមេរៀនសហគមន៍"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"អូសទៅឆ្វេង ដើម្បីចាប់ផ្ដើមមេរៀនសហគមន៍"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"បើកផ្ទាំងជ្រើសរើសធាតុ​ក្រាហ្វិក"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ដកធាតុក្រាហ្វិកចេញ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយ​ទាញចុះ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"បានប្រើនាពេលថ្មីៗនេះដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"កំពុងប្រើដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"បានប្រើនាពេលថ្មីៗនេះដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7447d5b6883e..080ce108e46c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ಬ್ಯಾಟರಿ ಶೇಕಡಾವಾರು ತಿಳಿದಿಲ್ಲ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"ಸಮುದಾಯದ ಟುಟೋರಿಯಲ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಆ್ಯರೋ ಬಟನ್ ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ಸಮುದಾಯದ ಟ್ಯುಟೋರಿಯಲ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ವಿಜೆಟ್ ಪಿಕರ್‌ ತೆರೆಯಿರಿ"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ವಿಜೆಟ್ ತೆಗೆದುಹಾಕಿ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್‌ಡೌನ್ ಮೆನು"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್‌ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"ಧ್ವನಿ &amp; ವೈಬ್ರೇಷನ್"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</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_system_lowered_text" product="default" msgid="1250251883692996888">"ಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ದೀರ್ಘಕಾಲ ಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್‌ ಹೆಚ್ಚಿಗೆ ಇದೆ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್ ಈ ವಾರದ‌ ಮಟ್ಟಿಗೆ ಸುರಕ್ಷಿತ ಮಿತಿಯನ್ನು ಮೀರಿದೆ"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"ಆಲಿಸುತ್ತಿರಿ"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"ವಾಲ್ಯೂಮ್ ತಗ್ಗಿಸಿ"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಂದ ಬಳಕೆಯಲ್ಲಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 885966032b86..917781662a20 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"배터리 잔량을 알 수 없습니다."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>에 연결되었습니다."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>에 연결됨"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 고속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 저속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"공동 튜토리얼을 시작하려면 화살표 버튼을 클릭하세요."</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"공동 튜토리얼을 시작하려면 왼쪽으로 스와이프하세요"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -504,7 +516,7 @@
<string name="sound_settings" msgid="8874581353127418308">"소리 및 진동"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"설정"</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_system_lowered_text" product="default" msgid="1250251883692996888">"헤드폰 볼륨이 권장 시간보다 오래 높은 상태였습니다."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"헤드폰 볼륨이 이번 주 안전 한도를 초과했습니다."</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"볼륨 유지하기"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"볼륨 낮추기"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"최근 <xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용됨(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용 중(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"최근 <xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용됨(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ae4f4311b5b0..e58cf593bde7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth байланышта"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth түзмөгүнүн сүрөтчөсү"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Түзмөктүн чоо-жайын конфигурациялоо үчүн чыкылдатыңыз"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея кубатынын деңгээли белгисиз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> менен туташты."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Тез кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жай кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Жалпы үйрөткүчтү иштетүү үчүн жебе баскычын басыңыз"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Жалпы үйрөткүчтү иштетүү үчүн солго сүрүңүз"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -504,7 +516,7 @@
<string name="sound_settings" msgid="8874581353127418308">"Үн жана дирилдөө"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Параметрлер"</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_system_lowered_text" product="default" msgid="1250251883692996888">"Гарнитуранын үнүн катуу чыгарып, сунушталгандан узагыраак угуп жатасыз"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гарнитуранын үнүнүн катуулугу бул аптада коопсуз деңгээлден жогору болду"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Уга берем"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Үнүн акырындатуу"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Акыркы жолу <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) колдонмосунда иштетилди"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда иштетилип жатат (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Акыркы жолу <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) колдонмосунда иштетилди"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index a4c9417ef2c7..0c220cce1b86 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"ໄອຄອນອຸປະກອນ Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"ຄລິກເພື່ອຕັ້ງຄ່າລາຍລະອຽດອຸປະກອນ"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ບໍ່ຮູ້ເປີເຊັນແບັດເຕີຣີ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ເຊື່ອມ​ຕໍ່​ຫາ <xliff:g id="BLUETOOTH">%s</xliff:g> ແລ້ວ."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"ເຊື່ອມຕໍ່ຫາ <xliff:g id="CAST">%s</xliff:g> ແລ້ວ."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄວ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"ຄລິກໃສ່ປຸ່ມລູກສອນເພື່ອເລີ່ມຕົ້ນສອນການນຳໃຊ້ຊຸມຊົນ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ປັດຊ້າຍເພື່ອເລີ່ມບົດແນະນຳສ່ວນກາງ"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ໃຊ້ຫຼ້າສຸດໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"ໃຊ້ຢູ່ໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ໃຊ້ຫຼ້າສຸດໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 772ff0e1b043..b1139a2bd4fc 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"„Bluetooth“ prijungtas."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"„Bluetooth“ įrenginio piktograma"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Spustelėkite, jei norite konfigūruoti išsamią įrenginio informaciją"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumuliatoriaus energija procentais nežinoma."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Prisijungta prie „<xliff:g id="BLUETOOTH">%s</xliff:g>“."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Prisijungta prie <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sparčiai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Spustelėkite rodyklės mygtuką, kad paleistumėte bendruomenės mokomąją medžiagą"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Perbraukite kairėn, paleistumėte bendruomenės mokomąją medžiagą"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Neseniai naudojo „<xliff:g id="APP_NAME">%1$s</xliff:g>“ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Naudoja <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Neseniai naudojo „<xliff:g id="APP_NAME">%1$s</xliff:g>“ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9987f48c5783..03d6237f9125 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth savienojums ir izveidots."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth ierīces ikona"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Lai konfigurētu ierīces informāciju, noklikšķiniet"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumulatora uzlādes līmenis procentos nav zināms."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ir izveidots savienojum ar <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Savienots ar ierīci <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ātrā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lēnā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Noklikšķiniet uz bultiņas pogas, lai palaistu kopienas pamācību."</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Velciet pa kreisi, lai palaistu kopienas pamācību."</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nesen to izmantoja lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"To izmanto lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nesen to izmantoja lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 273443f9bed6..3374ba335b44 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е поврзан."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Икона за уред со Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Кликнете за да ги конфигурирате деталите за уредот"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентот на батеријата е непознат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Поврзано со <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Поврзано со <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни брзо • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Кликнете на копчето со стрелка за да го започнете заедничкото упатство"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Повлечете налево за да го започнете заедничкото упатство"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
@@ -503,8 +515,8 @@
<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="csd_lowered_title" product="default" msgid="2464112924151691129">"Звукот е намален на побезбедна вредност"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Јачината на звукот е висока подолго од препорачаното"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Продолжете со слушање"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Намалете го звукот"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Неодамна користено од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Се користи од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Неодамна користено од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index a521eee82fc8..6e70f84df206 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ബ്ലൂടൂത്ത് കണക്‌റ്റുചെയ്തു."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth ഉപകരണ ഐക്കൺ"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"ഉപകരണത്തിന്റെ വിശദാംശങ്ങൾ കോൺഫിഗർ ചെയ്യാൻ ക്ലിക്ക് ചെയ്യുക"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ബാറ്ററി ശതമാനം അജ്ഞാതമാണ്."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> എന്നതിലേക്ക് കണക്‌റ്റുചെയ്‌തു."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്തു."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്‌റ്റ് ചെയ്‌തു"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"കമ്മ്യൂണൽ ട്യൂട്ടോറിയൽ ആരംഭിക്കാൻ അമ്പടയാള ബട്ടണിൽ ക്ലിക്ക് ചെയ്യുക"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"കമ്മ്യൂണൽ ട്യൂട്ടോറിയൽ ആരംഭിക്കാൻ ഇടത്തോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"വിജറ്റ് പിക്കർ തുറക്കുക"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"വിജറ്റ് നീക്കം ചെയ്യുക"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) അടുത്തിടെ ഉപയോഗിച്ചു"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ഉപയോഗിക്കുന്നു"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) അടുത്തിടെ ഉപയോഗിച്ചു"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7e58affa7041..93538c119eb6 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth холбогдсон."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth төхөөрөмжийн дүрс тэмдэг"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Төхөөрөмжийн дэлгэрэнгүйг тохируулахын тулд товшино уу"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарейн хувь тодорхойгүй байна."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>-тай холбогдсон."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>-д холбогдсон."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Хурдтай цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Нийтийн практик хичээлийг эхлүүлэхийн тулд суман товчийг товшино уу"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Нийтийн практик хичээлийг эхлүүлэхийн тулд зүүн тийш шударна уу"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Саяхан <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ашигласан"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ашиглаж байна"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Саяхан <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ашигласан"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4f0b947bab0a..32c27ac0e828 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"बॅटरीच्या चार्जिंगची टक्केवारी माहित नाही."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> शी कनेक्‍ट केले."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> शी कनेक्ट केले."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्‍लूटूथ वापरा"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वेगाने चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • हळू चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"सामुदायिक ट्यूटोरियल सुरू करण्यासाठी ॲरो बटणावर क्लिक करा"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"सामुदायिक ट्यूटोरियल सुरू करण्यासाठी डावीकडे स्वाइप करा"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"विजेट पिकर उघडा"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"विजेट काढून टाका"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"अलीकडे <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने वापरले"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) द्वारे वापरले जात आहे"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"अलीकडे <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ने वापरले"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 514e5aca8dbf..62efce1cd8f0 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth disambungkan."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikon peranti Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klik untuk mengkonfigurasi butiran peranti"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Peratusan kuasa bateri tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Disambungkan kepada <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Disambungkan ke <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan cepat • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan perlahan • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klik butang anak panah untuk memulakan tutorial umum"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Leret ke kiri untuk memulakan tutorial umum"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Buka pemilih widget"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Alih keluar widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Digunakan baru-baru ini oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Digunakan baru-baru ini oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 0a3870192960..6dd247dd71a6 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ဘက်ထရီရာခိုင်နှုန်းကို မသိပါ။"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>သို့ ချိတ်ဆက်ထား"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> သို့ချိတ်ဆက်ထားပါသည်။"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အမြန်အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"အများသုံးရှင်းလင်းပို့ချချက် စတင်ရန် မြားခလုတ်ကို နှိပ်ပါ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"အများသုံးရှင်းလင်းပို့ချချက် စတင်ရန် ဘယ်သို့ပွတ်ဆွဲပါ"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -503,8 +515,8 @@
<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="csd_lowered_title" product="default" msgid="2464112924151691129">"အသံကို ဘေးကင်းသည့်အဆင့်သို့ တိုးထားသည်"</string>
- <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"နားကြပ်အသံအား အကြံပြုထားသည်ထက် ပိုကြာရှည်စွာ ချဲ့ထားသည်"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"ဆက်နားဆင်ရန်"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"အသံတိုးရန်"</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) က လတ်တလောသုံးထားသည်"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) က သုံးနေသည်"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) က လတ်တလောသုံးထားသည်"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index db181ad8f2c4..f873274d9f95 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth er tilkoblet."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikon for Bluetooth-enheter"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klikk for å konfigurere enhetsdetaljer"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriprosenten er ukjent."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Koblet til <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Koblet til <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader raskt • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klikk på pilen for å starte fellesveiledningen"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Sveip til venstre for å starte fellesveiledningen"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Åpne modulvelgeren"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Fjern en modul"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nylig brukt av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"I bruk av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nylig brukt av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 30c972280c71..24d7ffe5372d 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ब्याट्रीमा कति प्रतिशत चार्ज छ भन्ने कुराको जानाकरी छैन।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> मा जडित।"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> मा कनेक्ट गरियो।"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • छिटो चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • बिस्तारै चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा फुल चार्ज हुने छ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"कम्युनल ट्युटोरियल सुरु गर्न एरो बटनमा क्लिक गर्नुहोस्"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्युनल ट्युटोरियल सुरु गर्न बायाँतिर स्वाइप गर्नुहोस्"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"विजेट पिकर खोल्नुहोस्"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"कुनै विजेट हटाउनुहोस्"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"साउन्ड तथा भाइब्रेसन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिङ"</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_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफोनको भोल्युम धेरै बेरदेखि उच्च छ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"यो हप्ता हेडफोनको भोल्युमले सुरक्षित स्तरको सीमा नाघेको छ"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"सुनिराख्नुहोस्"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"भोल्युम घटाउनुहोस्"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ले हालसालै प्रयोग गरेको"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ले प्रयोग गरिरहेको छ"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ले हालसालै प्रयोग गरेको"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index dc654a556251..9a110a251aba 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-verbinding ingesteld."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icoon voor bluetooth-apparaat"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klik om de apparaatgegevens in te stellen"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterijpercentage onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Verbonden met <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Verbonden met <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Snel opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klik op de pijl om de communitytutorial te starten"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe naar links om de communitytutorial te starten"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Open de widgetkiezer"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Verwijder een widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recent gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recent gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index bc2cef0616b3..a7726277eab9 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ବ୍ୟାଟେରୀ ଶତକଡ଼ା ଅଜଣା ଅଟେ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ସହ ସଂଯୁକ୍ତ"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> ସହିତ ସଂଯୁକ୍ତ।"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"କମ୍ୟୁନାଲ ଟ୍ୟୁଟୋରିଆଲ ଆରମ୍ଭ କରିବା ପାଇଁ ତୀର ବଟନରେ କ୍ଲିକ କରନ୍ତୁ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"କମ୍ୟୁନାଲ ଟ୍ୟୁଟୋରିଆଲ ଆରମ୍ଭ କରିବା ପାଇଁ ବାମକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ୱିଜେଟ ପିକର ଖୋଲନ୍ତୁ"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ଏକ ୱିଜେଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
@@ -408,7 +418,7 @@
<string name="guest_notification_session_active" msgid="5567273684713471450">"ଆପଣ ଅତିଥି ମୋଡରେ ଅଛନ୍ତି"</string>
<string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ଜଣେ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରିବା ଦ୍ୱାରା ଅତିଥି ମୋଡରୁ ବାହାରି ଯିବ ଏବଂ ବର୍ତ୍ତମାନର ଅତିଥି ସେସନରୁ ସମସ୍ତ ଆପ ଓ ଡାଟା ଡିଲିଟ ହୋଇଯିବ।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ଉପଯୋଗକର୍ତ୍ତା ସୀମାରେ ପହଞ୍ଚିଛି"</string>
- <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{କେବଳ ଜଣେ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଇପାରିବ।}other{କେବଳ # ଜଣ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଇପାରିବ।}}"</string>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{କେବଳ ଜଣେ ୟୁଜର ତିଆରି କରାଯାଇପାରିବ।}other{କେବଳ # ଜଣ ୟୁଜର ତିଆରି କରାଯାଇପାରିବ।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ୟୁଜରଙ୍କୁ ବାହାର କରିବେ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ଏହି ୟୁଜରଙ୍କ ସମସ୍ତ ଆପ୍‍ ଓ ଡାଟା ଡିଲିଟ୍‍ ହେବ।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେସନ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ସେଟିଂସ"</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_system_lowered_text" product="default" msgid="1250251883692996888">"ସୁପାରିଶ ଭଲ୍ୟୁମ ଠାରୁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ଏହି ସପ୍ତାହ ପାଇଁ ହେଡଫୋନର ଭଲ୍ୟୁମ ସୁରକ୍ଷିତ ସୀମାକୁ ଅତିକ୍ରମ କରିଛି"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"ଶୁଣିବା ଜାରି ରଖନ୍ତୁ"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"ଭଲ୍ୟୁମ କମାନ୍ତୁ"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 66c3e4699647..bb956b6bc0a1 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ਕਨੈਕਟ ਕੀਤੀ।"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"ਬਲੂਟੁੱਥ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"ਡੀਵਾਈਸ ਦੇ ਵੇਰਵੇ ਦਾ ਸੰਰੂਪਣ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ਬੈਟਰੀ ਪ੍ਰਤੀਸ਼ਤ ਅਗਿਆਤ ਹੈ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ।"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ।"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"ਭਾਈਚਾਰਕ ਟਿਊਟੋਰੀਅਲ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਤੀਰ ਬਟਨ \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ਭਾਈਚਾਰਕ ਟਿਊਟੋਰੀਅਲ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ਵਿਜੇਟ ਚੋਣਕਾਰ ਨੂੰ ਖੋਲ੍ਹੋ"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ਵਿਜੇਟ ਨੂੰ ਹਟਾਓ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ਵੱਲੋਂ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 589c2a318e93..a28329f74a14 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth połączony."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona urządzenia Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknij, aby skonfigurować szczegóły urządzenia"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Poziom naładowania baterii jest nieznany."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Połączono z <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Połączono z urządzeniem <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Użyj Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Szybkie ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknij przycisk strzałki, aby uruchomić samouczek społecznościowy"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aby uruchomić wspólny samouczek, przeciągnij palcem w lewo"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ostatnio używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ostatnio używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index f9f247af7aef..0c0aafa6651e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ícone de dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Clique para configurar os detalhes do dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregamento rápido • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Clique no botão de seta para iniciar o tutorial compartilhado"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Abrir o seletor de widgets"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remover um widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -504,7 +514,7 @@
<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="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 do fones de ouvido está alto há mais tempo que o recomendado"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Continuar ouvindo"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Diminuir o volume"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b445d0897e43..0cfc7aaecc81 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ligado."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ícone de dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Clique para configurar o detalhe do dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ligado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ligado a <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar rapidamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Clique no botão de seta para iniciar o tutorial coletivo"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize rapidamente para a esquerda para iniciar o tutorial coletivo"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Abrir seletor de widgets"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remover widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em utilização pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index f9f247af7aef..0c0aafa6651e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ícone de dispositivo Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Clique para configurar os detalhes do dispositivo"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregamento rápido • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Clique no botão de seta para iniciar o tutorial compartilhado"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Abrir o seletor de widgets"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Remover um widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -504,7 +514,7 @@
<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="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 do fones de ouvido está alto há mais tempo que o recomendado"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Continuar ouvindo"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Diminuir o volume"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 563d8e1a22e2..4df30fa5636b 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Conectat prin Bluetooth."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Pictograma de dispozitiv Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Dă clic pentru a configura detaliile dispozitivului"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procentajul bateriei este necunoscut."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectat la <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"S-a stabilit conexiunea la <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă rapid • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Dă clic pe butonul săgeată pentru a începe tutorialul pentru comunitate"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Glisează spre stânga pentru a începe tutorialul pentru comunitate"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Se folosește pentru <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index e1ac3649dbe2..c29dc11b5f5b 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-соединение установлено."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Значок устройства Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Нажмите, чтобы изменить информацию об устройстве"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Уровень заряда батареи в процентах неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>: подключено."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Подключено к: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Быстрая зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Медленная зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Нажмите кнопку со стрелкой, чтобы ознакомиться с руководством"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Чтобы ознакомиться с руководством, проведите по экрану влево"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Недавно использовалось приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Сейчас используется приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Недавно использовалось приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index edcf4156b10b..68bd1ce3413e 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"බැටරි ප්‍රතිශතය නොදනී."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> වෙත සම්බන්ධ කරන ලදි."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> වෙත සම්බන්ධ විය."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • සෙමින් ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"වාර්ගික නිබන්ධනය ආරම්භ කිරීමට ඊතල බොත්තම ක්ලික් කරන්න"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"පොදු නිබන්ධනය ආරම්භ කිරීමට වමට ස්වයිප් කරන්න"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් මෑතකදී භාවිත කරන ලදි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> භාවිත කරයි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් මෑතකදී භාවිත කරන ලදි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d95bde151bb9..f916bf272ba3 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth pripojené."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona zariadenia s rozhraním Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknutím nakonfigurujte podrobnosti o zariadení"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percento batérie nie je známe."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Pripojené k zariadeniu <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Pripojené k zariadeniu <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Použiť Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa rýchlo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Ak chcete spustiť komunitný návod, kliknite na tlačidlo so šípkou"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Potiahnutím doľava spustite komunitný návod"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno využila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Využíva <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno využila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index cc6040f602de..72de807d4c04 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Povezava Bluetooth vzpostavljena."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona naprave Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliknite za konfiguriranje podrobnosti o napravi"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Neznan odstotek napolnjenosti baterije."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezava vzpostavljena z: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Vzpostavljena povezava: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hitro polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Počasno polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliknite gumb s puščico, da zaženete vadnico za skupnost"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Povlecite levo, da zaženete vadnico za skupnost"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno uporabljala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Uporablja aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno uporabljala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index e214f4a62f9e..2c1f6ac50f66 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Pajisja është lidhur me \"bluetooth\"."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Ikona e pajisjes me Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Kliko për të konfiguruar detajet e pajisjes"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Përqindja e baterisë e panjohur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Lidhur me <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Është lidhur me <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet shpejt • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Kliko mbi butonin e shigjetës për të filluar udhëzuesin e përbashkët"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Rrëshqit shpejt majtas për të filluar udhëzuesin e përbashkët"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Përdorur së fundi nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Në përdorim nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Përdorur së fundi nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index d2512320ba39..428cc5f71d07 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth је прикључен."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Икона Bluetooth уређаја"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Кликните да бисте конфигурисали детаље о уређају"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Проценат напуњености батерије није познат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Повезани сте са <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Повезани смо са уређајем <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Брзо се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Кликните на дугме са стрелицом да бисте започели заједнички водич"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Превуците улево да бисте започели заједнички водич"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Отвори бирач виџета"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Уклони виџет"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Недавно користила апликација <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Користе <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Недавно користила апликација <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b870a1d1eb60..179ed6e77714 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ansluten."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Enhetsikon för Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Klicka för att konfigurera enhetsinformation"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Okänd batterinivå."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ansluten till <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ansluten till <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas snabbt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Klicka på pilknappen för att börja med gruppguiden"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Svep åt vänster för att börja med gruppguiden"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Användes nyligen av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Används av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Användes nyligen av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 57bedbf61fb9..36aa7d56b767 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth imeunganishwa."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Aikoni ya Kifaa chenye Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Bofya ili uweke mipangilio ya maelezo ya kifaa"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Asilimia ya betri haijulikani."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Imeunganishwa kwenye <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Imeunganishwa kwenye <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji kwa kasi • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji polepole • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Bofya kwenye kitufe cha kishale ili kuanzisha mafunzo ya jumuiya"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Telezesha kidole kushoto ili uanze mafunzo ya pamoja"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Fungua kiteua wijeti"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Ondoa wijeti"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Inatumiwa na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9342ce9f691f..1f3264635589 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"பேட்டரி சதவீதம் தெரியவில்லை."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>க்கு இணைக்கப்பட்டது."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • வேகமாகச் சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • மெதுவாக சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுவதும் சார்ஜாகும்"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"சமூகப் பயிற்சியைத் தொடங்க அம்புக்குறி பட்டனைக் கிளிக் செய்யுங்கள்"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"சமூகப் பயிற்சியைத் தொடங்க இடதுபுறம் ஸ்வைப் செய்யுங்கள்"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ஆப்ஸால் பயன்படுத்தப்படுகிறது"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index e9f07af16aa8..e7ba58349d04 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"బ్యాటరీ శాతం తెలియదు."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ చేయబడింది"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"కమ్యూనల్ ట్యుటోరియల్‌ను ప్రారంభించడానికి బాణం బటన్‌పై క్లిక్ చేయండి"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"కమ్యూనల్ ట్యుటోరియల్‌ను ప్రారంభించడానికి ఎడమ వైపునకు స్వైప్ చేయండి"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్‌డౌన్ మెనూ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ద్వారా ఇటీవల వినియోగించబడింది"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ద్వారా వినియోగంలో ఉంది"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ద్వారా ఇటీవల ఉపయోగించబడింది"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 82d8118c663e..1651e540f72a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ไม่ทราบเปอร์เซ็นต์แบตเตอรี่"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"เชื่อมต่อกับ <xliff:g id="BLUETOOTH">%s</xliff:g> แล้ว"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"เชื่อมต่อกับ <xliff:g id="CAST">%s</xliff:g>"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างเร็ว • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างช้าๆ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"คลิกปุ่มลูกศรเพื่อเริ่มบทแนะนำส่วนกลาง"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ปัดไปทางซ้ายเพื่อเริ่มบทแนะนำส่วนกลาง"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"เปิดเครื่องมือเลือกวิดเจ็ต"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"นำวิดเจ็ตออก"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ใช้ล่าสุดโดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"ใช้อยู่โดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ใช้ล่าสุดโดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 211a747483fc..085fdeed18ab 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Nakakonekta ang Bluetooth."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Icon ng Bluetooth device"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"I-click para i-configure ang detalye ng device"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Hindi alam ang porsyento ng baterya."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Nakakonekta sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Nakakonekta sa <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabilis na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabagal na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"I-click ang arrow button para simulan ang communal na tutorial"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Mag-swipe pakaliwa para simulan ang communal na tutorial"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Buksan ang picker ng widget"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Mag-alis ng widget"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kamakailang ginamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Ginagamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kamakailang ginamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e0fac50f9f8e..820ccd17f4d4 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth bağlandı."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth cihaz simgesi"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Cihaz ayrıntılarını yapılandırmak için tıklayın"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pil yüzdesi bilinmiyor."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ile bağlı."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> bağlantısı kuruldu."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hızlı şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Yavaş şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Ortak eğitimi başlatmak için ok düğmesini tıklayın"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi başlatmak için sola kaydırın"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -506,7 +518,7 @@
<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>
- <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Dinlemeye devam"</string>
+ <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Dinlemeye devam edin"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Sesi kıs"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Uygulama sabitlendi"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Bu işlem, siz sabitlemeyi kaldırana kadar ekranı görünür durumda tutar. Sabitlemeyi kaldırmak için Geri\'ye ve Genel Bakış\'a dokunup basılı tutun."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En son <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) tarafından kullanıldı"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) tarafından kullanılıyor"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En son <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) tarafından kullanıldı"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a2c4ef5a11bf..ea3d0b1ed597 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth під’єднано."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Значок пристрою з Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Натисніть, щоб змінити налаштування пристрою"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Відсоток заряду акумулятора невідомий."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Підключено до <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Під’єднано до пристрою <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Швидке заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Повільне заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Натисніть кнопку зі стрілкою, щоб відкрити спільний навчальний посібник"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Проведіть пальцем уліво, щоб відкрити спільний навчальний посібник"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Використовується додатком <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 364a84cadc7b..bf58d9c28fd4 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"بیٹری کی فیصد نامعلوم ہے۔"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> سے منسلک ہیں۔"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> سے منسلک ہے۔"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استعمال کریں"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • تیزی سے چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • آہستہ چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"کمیونل ٹیوٹوریل شروع کرنے کے لیے تیر کے نشان والے بٹن پر کلک کریں"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"کمیونل ٹیوٹوریل شروع کرنے کے لیے بائیں سوائپ کریں"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"ویجیٹ چنندہ کو کھولیں"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"ویجیٹ ہٹائیں"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) کے زیر استعمال"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3ff1808aee1d..0e04cd5fd191 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -196,7 +196,10 @@
<string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Yuz bilan ochilmaydi."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ulandi."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth qurilma belgisi"</string>
- <!-- no translation found for accessibility_bluetooth_device_settings_gear (3314916468105272540) -->
+ <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Qurilma haqida tafsilotlarni oʻzgartirish uchun bosing"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
<skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareya quvvati foizi nomaʼlum."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ulangan: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -256,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulandi"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -397,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Tez quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sekin quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Qoʻllanma bilan tanishish uchun strelka tugmasini bosing"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Qoʻllanma bilan tanishish uchun chapga suring"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"Vidjet tanlash vositasini ochish"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"Vidjetni olib tashlash"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -1181,8 +1190,7 @@
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera va mikrofon bloklangan"</string>
<string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon bloklangan"</string>
<string name="priority_mode_dream_overlay_content_description" msgid="6044561000253314632">"Imtiyozli rejim yoniq"</string>
- <!-- no translation found for assistant_attention_content_description (4166330881435263596) -->
- <skip />
+ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Foydalanuvchi aniqlandi"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string>
<string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string>
@@ -1207,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Yaqinda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ishlatgan"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ishlatmoqda"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Yaqinda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ishlatgan"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 92fefc618322..31350be4091b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Đã kết nối bluetooth."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Biểu tượng thiết bị Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Nhấp để định cấu hình thông tin thiết bị"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Tỷ lệ phần trăm pin không xác định."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Đã kết nối với <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Đã kết nối với <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc nhanh • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc chậm • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Nhấp vào nút mũi tên để bắt đầu xem hướng dẫn chung"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Vuốt sang trái để bắt đầu xem hướng dẫn chung"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) đã dùng gần đây"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đang dùng"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đã dùng gần đây"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 712f1a9d1f3b..4db1a91b1ba7 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"电池电量百分比未知。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已连接到<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已连接到 <xliff:g id="CAST">%s</xliff:g>。"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用蓝牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在快速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在慢速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"点击箭头按钮,即可启动公共教程"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑动即可启动公共教程"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"打开微件选择器"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"移除微件"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -503,7 +513,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="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已降到更安全的水平"</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>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"继续聆听"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正在使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 31abf82cd817..125cfe15164a 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電量百分比不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已連接至 <xliff:g id="CAST">%s</xliff:g>。"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"按一下箭咀鍵,即可開始共用教學課程"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可開始共用教學課程"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"開啟小工具挑選器"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"移除小工具"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近使用過此權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 正在使用 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近使用過此權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d112ad2a7501..dba68c500dc0 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -197,6 +197,10 @@
<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>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電池電量不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"已連線至 <xliff:g id="CAST">%s</xliff:g>。"</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,9 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"點選箭頭按鈕,即可開始通用教學課程"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可啟動通用教學課程"</string>
+ <string name="button_to_open_widget_picker" msgid="8007261659745030810">"開啟小工具挑選器"</string>
+ <string name="button_to_remove_widget" msgid="1511255853677835341">"移除小工具"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string>
@@ -504,7 +514,7 @@
<string name="sound_settings" msgid="8874581353127418308">"音效與震動"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</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_system_lowered_text" product="default" msgid="1250251883692996888">"耳機以高音量播放已超過建議時間"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳罩式耳機的音量已超過本週的安全限制"</string>
<string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"繼續聆聽"</string>
<string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"調低音量"</string>
@@ -1205,4 +1215,6 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近用過這項權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"正由「<xliff:g id="APP_NAME">%1$s</xliff:g>」使用 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近用過這項權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
+ <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d1bdc15ecf6b..982ca4a12dd8 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -197,6 +197,10 @@
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ixhunyiwe"</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Isithonjana sedivayisi ye-Bluetooth"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Chofoza ukuze ulungiselele imininingwane yedivayisi"</string>
+ <!-- no translation found for accessibility_bluetooth_device_settings_see_all (9111952496905423543) -->
+ <skip />
+ <!-- no translation found for accessibility_bluetooth_device_settings_pair_new_device (2435184865793496966) -->
+ <skip />
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Iphesenti lebhethri alaziwa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Xhuma ku-<xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Ixhumeke ku-<xliff:g id="CAST">%s</xliff:g>."</string>
@@ -255,6 +259,10 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa i-Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_disconnect (415980329093277342) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_bluetooth_device_tap_to_activate (3724301751036877403) -->
+ <skip />
<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>
@@ -396,7 +404,11 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja ngokushesha • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja kancane • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="communal_tutorial_indicator_text" msgid="700342473477865107">"Chofoza inkinobho yomcibisholo ukuze uqalise isifundo somphakathi"</string>
+ <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swayiphela kwesokunxele ukuze uqale okokufundisa komphakathi"</string>
+ <!-- no translation found for button_to_open_widget_picker (8007261659745030810) -->
+ <skip />
+ <!-- no translation found for button_to_remove_widget (1511255853677835341) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -1205,4 +1217,8 @@
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kusetshenziswe kamuva yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Isetshenziswa yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kusetshenziswe kamuva yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <!-- no translation found for keyboard_backlight_dialog_title (8273102932345564724) -->
+ <skip />
+ <!-- no translation found for keyboard_backlight_value (7336398765584393538) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 74d435d18823..1838795a57d6 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -230,7 +230,6 @@
<!-- Communal mode -->
<item type="id" name="communal_hub" />
- <item type="id" name="communal_widget_wrapper" />
<!-- Values assigned to the views in Biometrics Prompt -->
<item type="id" name="pin_pad"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8780f58743cd..daf6cb3d683d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -467,6 +467,10 @@
<!-- Content description of the bluetooth device settings gear icon. [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_device_settings_gear">Click to configure device detail</string>
+ <!-- Content description of the bluetooth device settings see all. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_settings_see_all">Click to see all devices</string>
+ <!-- Content description of the bluetooth device settings pair new device. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_settings_pair_new_device">Click to pair new device</string>
<!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_unknown">Battery percentage unknown.</string>
@@ -640,6 +644,10 @@
<string name="quick_settings_bluetooth_device_connected">Connected</string>
<!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]-->
<string name="quick_settings_bluetooth_device_saved">Saved</string>
+ <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]-->
+ <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect">disconnect</string>
+ <!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]-->
+ <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string>
<!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
@@ -1048,10 +1056,12 @@
<!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
<string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
- <!-- Description for the button that opens the widget picker on click. [CHAR LIMIT=50] -->
- <string name="button_to_open_widget_picker">Open the widget picker</string>
+ <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] -->
+ <string name="button_to_open_widget_editor">Open the widget editor</string>
<!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->
<string name="button_to_remove_widget">Remove a widget</string>
+ <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] -->
+ <string name="hub_mode_add_widget_button_text">Add Widget</string>
<!-- Related to user switcher --><skip/>
@@ -3266,4 +3276,9 @@
<string name="privacy_dialog_active_app_usage_2">In use by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
<!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] -->
<string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
+
+ <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
+ <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
+ <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
+ <string name="keyboard_backlight_value">Level %1$d of %2$d</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7ce530fce470..befee2b3eeb7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -943,6 +943,11 @@
<item name="android:windowLightStatusBar">true</item>
</style>
+ <style name="Theme.EditWidgetsActivity"
+ parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="android:windowBackground">@android:color/white</item>
+ </style>
+
<style name="TextAppearance.Control">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
@@ -1491,10 +1496,14 @@
<style name="ShortCutButton" parent="@android:style/Widget.Material.Button">
<item name="android:background">@drawable/shortcut_button_colored</item>
- <item name="android:stateListAnimator">@null</item>
<item name="android:textSize">16sp</item>
- <item name="android:padding">4dp</item>
- <item name="android:textColor">?androidprv:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:layout_marginEnd">12dp</item>
+ <item name="android:paddingLeft">24dp</item>
+ <item name="android:paddingRight">24dp</item>
+ <item name="android:minHeight">40dp</item>
+ <item name="android:stateListAnimator">@*android:anim/flat_button_state_list_anim_material</item>
+ <item name="android:pointerIcon">arrow</item>
</style>
<style name="ShortcutHorizontalDivider">
@@ -1530,4 +1539,4 @@
<style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog">
<item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item>
</style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 6082fb93aa26..8ad32b4f1695 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -25,7 +25,11 @@ enum class FingerprintSensorType {
UDFPS_ULTRASONIC,
UDFPS_OPTICAL,
POWER_BUTTON,
- HOME_BUTTON
+ HOME_BUTTON;
+
+ fun isUdfps(): Boolean {
+ return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
+ }
}
/** Convert [this] to corresponding [FingerprintSensorType] */
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 6b390b1191d2..c02ffa788d48 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -39,10 +39,10 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_VIEW
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.LogBuffer
@@ -298,7 +298,7 @@ constructor(
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!featureFlags.isEnabled(MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
if (!isKeyguardVisible) {
clock?.run {
smallClock.animations.doze(if (isDozing) 1f else 0f)
@@ -345,7 +345,7 @@ constructor(
parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForDozing(this)
- if (featureFlags.isEnabled(MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
listenForDozeAmountTransition(this)
listenForAnyStateToAodTransition(this)
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index c5a06b48e015..8ed675c61edf 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.content.Context;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -57,7 +58,10 @@ public class EmergencyButton extends Button {
super.onFinishInflate();
if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
setOnLongClickListener(v -> {
- if (!mLongPressWasDragged
+ boolean isEmergencyCallButton = getVisibility() == View.VISIBLE
+ && TextUtils.equals(getText(), getEmergencyButtonLabel());
+ if (isEmergencyCallButton
+ && !mLongPressWasDragged
&& mEmergencyAffordanceManager.needsEmergencyAffordance()) {
mEmergencyAffordanceManager.performEmergencyCall();
return true;
@@ -122,4 +126,8 @@ public class EmergencyButton extends Button {
setVisibility(View.GONE);
}
}
+
+ private String getEmergencyButtonLabel() {
+ return mContext.getString(com.android.internal.R.string.lockscreen_emergency_call);
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 32f9c3057753..1fa55f5d839b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.common.ui.ConfigurationState;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
@@ -46,6 +47,7 @@ import com.android.systemui.flags.Flags;
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.binder.KeyguardRootViewBinder;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
@@ -62,6 +64,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -76,6 +79,7 @@ import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.Locale;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -105,6 +109,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final DozeParameters mDozeParameters;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
+ private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -133,6 +138,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private KeyguardInteractor mKeyguardInteractor;
private KeyguardClockInteractor mKeyguardClockInteractor;
private final DelayableExecutor mUiExecutor;
+ private final Executor mBgExecutor;
private boolean mCanShowDoubleLineClock = true;
private DisposableHandle mAodIconsBindHandle;
@Nullable private NotificationIconContainer mAodIconContainer;
@@ -179,9 +185,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
LockscreenSmartspaceController smartspaceController,
ConfigurationController configurationController,
ScreenOffAnimationController screenOffAnimationController,
+ StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main DelayableExecutor uiExecutor,
+ @Background Executor bgExecutor,
DumpManager dumpManager,
ClockEventController clockEventController,
@KeyguardClockLog LogBuffer logBuffer,
@@ -202,8 +210,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mSmartspaceController = smartspaceController;
mConfigurationController = configurationController;
mScreenOffAnimationController = screenOffAnimationController;
+ mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
+ mBgExecutor = bgExecutor;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mDumpManager = dumpManager;
mClockEventController = clockEventController;
@@ -323,19 +333,22 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
updateAodIcons();
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
- false, /* notifyForDescendants */
- mDoubleLineClockObserver,
- UserHandle.USER_ALL
- );
-
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
- false, /* notifyForDescendants */
- mShowWeatherObserver,
- UserHandle.USER_ALL
- );
+ mBgExecutor.execute(() -> {
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
+ false, /* notifyForDescendants */
+ mDoubleLineClockObserver,
+ UserHandle.USER_ALL
+ );
+
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
+ false, /* notifyForDescendants */
+ mShowWeatherObserver,
+ UserHandle.USER_ALL
+ );
+ });
+
updateDoubleLineClock();
mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
@@ -362,7 +375,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
int getNotificationIconAreaHeight() {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
return 0;
} else if (NotificationIconContainerRefactor.isEnabled()) {
return mAodIconContainer != null ? mAodIconContainer.getHeight() : 0;
@@ -377,8 +390,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockEventController.unregisterListeners();
setClock(null);
- mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
- mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
+ mBgExecutor.execute(() -> {
+ mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
+ mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
+ });
mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
mKeyguardUnlockAnimationListener);
@@ -590,7 +605,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
private void updateAodIcons() {
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
com.android.systemui.res.R.id.left_aligned_notification_icon_container);
@@ -599,12 +614,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mAodIconsBindHandle.dispose();
}
if (nic != null) {
- nic.setOnLockScreen(true);
final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
nic,
mAodIconsViewModel,
mConfigurationState,
mConfigurationController,
+ mIconViewBindingFailureTracker,
mAodIconViewStore);
final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
nic,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 758d1fef6e2e..87d937bc45fb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -51,10 +51,9 @@ import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
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.ClockController;
@@ -100,7 +99,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
@@ -136,7 +134,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
KeyguardLogger logger,
- FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
KeyguardInteractor keyguardInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -149,9 +146,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mConfigurationController = configurationController;
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
- featureFlags, logger.getBuffer());
+ logger.getBuffer());
mInteractionJankMonitor = interactionJankMonitor;
- mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
@@ -188,7 +184,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
mDumpManager.registerDumpable(getInstanceName(), this);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
startCoroutines(EmptyCoroutineContext.INSTANCE);
}
}
@@ -454,7 +450,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(layout);
int guideline;
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
guideline = R.id.split_shade_guideline;
} else {
guideline = R.id.qs_edge_guideline;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index d524e4a8c408..ef6514447561 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -23,8 +23,7 @@ import android.util.Property;
import android.view.View;
import com.android.app.animation.Interpolators;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
+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;
@@ -55,7 +54,6 @@ public class KeyguardVisibilityHelper {
private boolean mKeyguardViewVisibilityAnimating;
private boolean mLastOccludedState = false;
private final AnimationProperties mAnimationProperties = new AnimationProperties();
- private final FeatureFlags mFeatureFlags;
private final LogBuffer mLogBuffer;
public KeyguardVisibilityHelper(View view,
@@ -63,14 +61,12 @@ public class KeyguardVisibilityHelper {
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
boolean animateYPos,
- FeatureFlags featureFlags,
LogBuffer logBuffer) {
mView = view;
mKeyguardStateController = keyguardStateController;
mDozeParameters = dozeParameters;
mScreenOffAnimationController = screenOffAnimationController;
mAnimateYPos = animateYPos;
- mFeatureFlags = featureFlags;
mLogBuffer = logBuffer;
}
@@ -167,7 +163,7 @@ public class KeyguardVisibilityHelper {
animProps,
true /* animate */);
} else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
log("Using GoneToAodTransition");
mKeyguardViewVisibilityAnimating = false;
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index f0915583f021..175fcdb6e11a 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -26,7 +26,7 @@ import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
+import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.annotation.SuppressLint;
@@ -60,10 +60,10 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -127,7 +127,7 @@ public class LockIconViewController implements Dumpable {
@NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
@NonNull private final KeyguardInteractor mKeyguardInteractor;
@NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
- @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor;
+ @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
@NonNull private final SceneContainerFlags mSceneContainerFlags;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -205,7 +205,7 @@ public class LockIconViewController implements Dumpable {
@NonNull FeatureFlags featureFlags,
PrimaryBouncerInteractor primaryBouncerInteractor,
Context context,
- Lazy<BouncerInteractor> bouncerInteractor,
+ Lazy<DeviceEntryInteractor> deviceEntryInteractor,
SceneContainerFlags sceneContainerFlags
) {
mStatusBarStateController = statusBarStateController;
@@ -232,7 +232,7 @@ public class LockIconViewController implements Dumpable {
dumpManager.registerDumpable(TAG, this);
mResources = resources;
mContext = context;
- mBouncerInteractor = bouncerInteractor;
+ mDeviceEntryInteractor = deviceEntryInteractor;
mSceneContainerFlags = sceneContainerFlags;
mAccessibilityDelegate = new View.AccessibilityDelegate() {
@@ -395,7 +395,7 @@ public class LockIconViewController implements Dumpable {
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
- } else if (mIsDozing && mFeatureFlags.isEnabled(NEW_AOD_TRANSITION)) {
+ } else if (mIsDozing && newAodTransition()) {
mView.animate()
.alpha(0f)
.setDuration(FADE_OUT_DURATION_MS)
@@ -747,7 +747,7 @@ public class LockIconViewController implements Dumpable {
vibrateOnLongPress();
if (mSceneContainerFlags.isEnabled()) {
- mBouncerInteractor.get().showOrUnlockDevice(null);
+ mDeviceEntryInteractor.get().attemptDeviceEntry();
} else {
mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt
new file mode 100644
index 000000000000..a068769cb515
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.keyguard.logging
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.ScrimLog
+import com.google.errorprone.annotations.CompileTimeConstant
+import javax.inject.Inject
+
+/**
+ * A logger to log scrim state.
+ *
+ * To enable logcat echoing for this buffer use this command:
+ * ```
+ * $ adb shell cmd statusbar echo -b ScrimLog:VERBOSE
+ * ```
+ */
+class ScrimLogger
+@Inject
+constructor(
+ @ScrimLog val buffer: LogBuffer,
+) {
+ companion object {
+ val TAG = ScrimLogger::class.simpleName!!
+ }
+
+ fun d(
+ tag: String,
+ @CompileTimeConstant msg: String,
+ arg: Any,
+ ) = log("$tag::$TAG", LogLevel.DEBUG, msg, arg)
+
+ fun log(
+ tag: String,
+ level: LogLevel,
+ @CompileTimeConstant msg: String,
+ arg: Any,
+ ) =
+ buffer.log(
+ tag,
+ level,
+ {
+ str1 = msg
+ str2 = arg.toString()
+ },
+ { "$str1: $str2" }
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 59b85d112753..b704f3c89330 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -59,8 +59,8 @@ import javax.inject.Inject;
* when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
*/
@SysUISingleton
-public class WindowMagnification implements CoreStartable, CommandQueue.Callbacks {
- private static final String TAG = "WindowMagnification";
+public class Magnification implements CoreStartable, CommandQueue.Callbacks {
+ private static final String TAG = "Magnification";
private final ModeSwitchesController mModeSwitchesController;
private final Context mContext;
@@ -154,7 +154,7 @@ public class WindowMagnification implements CoreStartable, CommandQueue.Callback
DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
@Inject
- public WindowMagnification(Context context, @Main Handler mainHandler,
+ public Magnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
@@ -366,49 +366,53 @@ public class WindowMagnification implements CoreStartable, CommandQueue.Callback
@VisibleForTesting
final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
new MagnificationSettingsController.Callback() {
- @Override
- public void onSetMagnifierSize(int displayId, int index) {
- mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
- mA11yLogger.logWithPosition(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
- index
- );
- }
+ @Override
+ public void onSetMagnifierSize(int displayId, int index) {
+ mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
+ mA11yLogger.logWithPosition(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
+ index
+ );
+ }
- @Override
- public void onSetDiagonalScrolling(int displayId, boolean enable) {
- mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
- }
+ @Override
+ public void onSetDiagonalScrolling(int displayId, boolean enable) {
+ mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
+ }
- @Override
- public void onEditMagnifierSizeMode(int displayId, boolean enable) {
- mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
- mA11yLogger.log(enable
- ? MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
- : MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
- }
+ @Override
+ public void onEditMagnifierSizeMode(int displayId, boolean enable) {
+ mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
+ mA11yLogger.log(enable
+ ?
+ MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
+ : MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
+ }
- @Override
- public void onMagnifierScale(int displayId, float scale, boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
- displayId, scale, updatePersistence);
- }
- mA11yLogger.logThrottled(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
- );
- }
+ @Override
+ public void onMagnifierScale(int displayId, float scale,
+ boolean updatePersistence) {
+ if (mWindowMagnificationConnectionImpl != null) {
+ mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ displayId, scale, updatePersistence);
+ }
+ mA11yLogger.logThrottled(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
+ );
+ }
- @Override
- public void onModeSwitch(int displayId, int newMode) {
- mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
- }
+ @Override
+ public void onModeSwitch(int displayId, int newMode) {
+ mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
+ }
- @Override
- public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
- mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
- }
- };
+ @Override
+ public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
+ mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
+ }
+ };
@MainThread
private void onSetMagnifierSizeInternal(int displayId, int index) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index ee7781d8af20..b4530ace68d6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -34,7 +34,7 @@ import com.android.systemui.util.settings.SecureSettings;
* A class to control {@link WindowMagnificationSettings} and receive settings panel callbacks by
* {@link WindowMagnificationSettingsCallback}.
* The settings panel callbacks will be delegated through
- * {@link MagnificationSettingsController.Callback} to {@link WindowMagnification}.
+ * {@link MagnificationSettingsController.Callback} to {@link Magnification}.
*/
public class MagnificationSettingsController implements ComponentCallbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 928445bde8ff..5666851f560f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -37,12 +37,12 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
private static final String TAG = "WindowMagnificationConnectionImpl";
private IWindowMagnificationConnectionCallback mConnectionCallback;
- private final WindowMagnification mWindowMagnification;
+ private final Magnification mMagnification;
private final Handler mHandler;
- WindowMagnificationConnectionImpl(@NonNull WindowMagnification windowMagnification,
+ WindowMagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
- mWindowMagnification = windowMagnification;
+ mMagnification = magnification;
mHandler = mainHandler;
}
@@ -51,56 +51,56 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
IRemoteMagnificationAnimationCallback callback) {
mHandler.post(
- () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
+ () -> mMagnification.enableWindowMagnification(displayId, scale, centerX,
centerY, magnificationFrameOffsetRatioX,
magnificationFrameOffsetRatioY, callback));
}
@Override
public void setScale(int displayId, float scale) {
- mHandler.post(() -> mWindowMagnification.setScale(displayId, scale));
+ mHandler.post(() -> mMagnification.setScale(displayId, scale));
}
@Override
public void disableWindowMagnification(int displayId,
IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
+ mHandler.post(() -> mMagnification.disableWindowMagnification(displayId,
callback));
}
@Override
public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
mHandler.post(
- () -> mWindowMagnification.moveWindowMagnifier(displayId, offsetX, offsetY));
+ () -> mMagnification.moveWindowMagnifier(displayId, offsetX, offsetY));
}
@Override
public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(() -> mWindowMagnification.moveWindowMagnifierToPositionInternal(
+ mHandler.post(() -> mMagnification.moveWindowMagnifierToPositionInternal(
displayId, positionX, positionY, callback));
}
@Override
public void showMagnificationButton(int displayId, int magnificationMode) {
mHandler.post(
- () -> mWindowMagnification.showMagnificationButton(displayId, magnificationMode));
+ () -> mMagnification.showMagnificationButton(displayId, magnificationMode));
}
@Override
public void removeMagnificationButton(int displayId) {
mHandler.post(
- () -> mWindowMagnification.removeMagnificationButton(displayId));
+ () -> mMagnification.removeMagnificationButton(displayId));
}
@Override
public void removeMagnificationSettingsPanel(int display) {
- mHandler.post(() -> mWindowMagnification.hideMagnificationSettingsPanel(display));
+ mHandler.post(() -> mMagnification.hideMagnificationSettingsPanel(display));
}
@Override
public void onUserMagnificationScaleChanged(int userId, int displayId, float scale) {
- mHandler.post(() -> mWindowMagnification.setUserMagnificationScale(
+ mHandler.post(() -> mMagnification.setUserMagnificationScale(
userId, displayId, scale));
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 9305ab6f6968..7ccf70427327 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -7,6 +7,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.SearchManager;
+import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -439,6 +440,14 @@ public class AssistManager {
public void onStartPerceiving() {
mAssistUtils.enableVisualQueryDetection(
mVisualQueryDetectionAttentionListener);
+ final StatusBarManager statusBarManager =
+ mContext.getSystemService(StatusBarManager.class);
+ if (statusBarManager != null) {
+ statusBarManager.setIcon("assist_attention",
+ R.drawable.ic_assistant_attention_indicator,
+ 0, "Attention Icon for Assistant");
+ statusBarManager.setIconVisibility("assist_attention", false);
+ }
}
@Override
@@ -447,11 +456,20 @@ public class AssistManager {
// accordingly).
handleVisualAttentionChanged(false);
mAssistUtils.disableVisualQueryDetection();
+ final StatusBarManager statusBarManager =
+ mContext.getSystemService(StatusBarManager.class);
+ if (statusBarManager != null) {
+ statusBarManager.removeIcon("assist_attention");
+ }
}
});
}
private void handleVisualAttentionChanged(boolean attentionGained) {
+ final StatusBarManager statusBarManager = mContext.getSystemService(StatusBarManager.class);
+ if (statusBarManager != null) {
+ statusBarManager.setIconVisibility("assist_attention", attentionGained);
+ }
mVisualQueryAttentionListeners.forEach(
attentionGained
? VisualQueryAttentionListener::onAttentionGained
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt
deleted file mode 100644
index 6d23b11e5d66..000000000000
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 com.android.systemui.authentication.data.model
-
-/** Enumerates all known authentication methods. */
-sealed class AuthenticationMethodModel(
- /**
- * Whether the authentication method is considered to be "secure".
- *
- * "Secure" authentication methods require authentication to unlock the device. Non-secure auth
- * methods simply require user dismissal.
- */
- open val isSecure: Boolean,
-) {
- /** There is no authentication method on the device. We shouldn't even show the lock screen. */
- object None : AuthenticationMethodModel(isSecure = false)
-
- object Pin : AuthenticationMethodModel(isSecure = true)
-
- object Password : AuthenticationMethodModel(isSecure = true)
-
- object Pattern : AuthenticationMethodModel(isSecure = true)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 80be008dbd3a..a42c0ae39c88 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -25,13 +25,14 @@ import com.android.internal.widget.LockPatternChecker
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
@@ -45,7 +46,9 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -65,7 +68,13 @@ interface AuthenticationRepository {
* Note that the length of the PIN is also important to take into consideration, please see
* [hintedPinLength].
*/
- val isAutoConfirmEnabled: StateFlow<Boolean>
+ val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
+
+ /**
+ * Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user
+ * in order to unlock the device.
+ */
+ val authenticationChallengeResult: SharedFlow<Boolean>
/**
* The exact length a PIN should be for us to enable PIN length hinting.
@@ -100,6 +109,12 @@ interface AuthenticationRepository {
/** The minimal length of a pattern. */
val minPatternLength: Int
+ /** The minimal length of a password. */
+ val minPasswordLength: Int
+
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean>
+
/**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge needs to be completed in order to unlock an otherwise locked device.
@@ -152,18 +167,20 @@ class AuthenticationRepositoryImpl
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
private val userRepository: UserRepository,
private val lockPatternUtils: LockPatternUtils,
broadcastDispatcher: BroadcastDispatcher,
+ mobileConnectionsRepository: MobileConnectionsRepository,
) : AuthenticationRepository {
- override val isAutoConfirmEnabled: StateFlow<Boolean> =
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
refreshingFlow(
initialValue = false,
getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
)
+ override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = 6
@@ -180,9 +197,11 @@ constructor(
get() = getSelectedUserInfo().id
override val authenticationMethod: Flow<AuthenticationMethodModel> =
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
+ combine(userRepository.selectedUserInfo, mobileConnectionsRepository.isAnySimSecure) {
+ selectedUserInfo,
+ _ ->
+ selectedUserInfo.id
+ }
.flatMapLatest { selectedUserId ->
broadcastDispatcher
.broadcastFlow(
@@ -200,9 +219,18 @@ constructor(
blockingAuthenticationMethodInternal(selectedUserId)
}
}
+ .distinctUntilChanged()
override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE
+ override val minPasswordLength: Int = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE
+
+ override val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ refreshingFlow(
+ initialValue = true,
+ getFreshValue = { userId -> lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) },
+ )
+
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return withContext(backgroundDispatcher) {
blockingAuthenticationMethodInternal(userRepository.selectedUserId)
@@ -224,6 +252,7 @@ constructor(
} else {
lockPatternUtils.reportFailedPasswordAttempt(selectedUserId)
}
+ authenticationChallengeResult.emit(isSuccessful)
}
}
@@ -335,9 +364,9 @@ constructor(
userId: Int,
): AuthenticationMethodModel {
return when (getSecurityMode.apply(userId)) {
- KeyguardSecurityModel.SecurityMode.PIN,
+ KeyguardSecurityModel.SecurityMode.PIN -> AuthenticationMethodModel.Pin
KeyguardSecurityModel.SecurityMode.SimPin,
- KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin
+ KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Sim
KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password
KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern
KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 345f15c5c6ad..c2974862bffb 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,19 +16,16 @@
package com.android.systemui.authentication.domain.interactor
-import com.android.app.tracing.TraceUtils.Companion.async
import com.android.app.tracing.TraceUtils.Companion.withContext
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -40,13 +37,14 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
/**
* Hosts application business logic related to user authentication.
@@ -63,7 +61,6 @@ constructor(
private val repository: AuthenticationRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
- private val deviceEntryRepository: DeviceEntryRepository,
private val clock: SystemClock,
) {
/**
@@ -84,8 +81,7 @@ constructor(
* `true` even when the lockscreen is showing and still needs to be dismissed by the user to
* proceed.
*/
- val authenticationMethod: Flow<DomainLayerAuthenticationMethodModel> =
- repository.authenticationMethod.map { rawModel -> rawModel.toDomainLayer() }
+ val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
/** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */
val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling
@@ -103,9 +99,29 @@ constructor(
initialValue = throttling.value.remainingMs > 0,
)
+ /**
+ * Whether the auto confirm feature is enabled for the currently-selected user.
+ *
+ * Note that the length of the PIN is also important to take into consideration, please see
+ * [hintedPinLength].
+ *
+ * During throttling, this is always disabled (`false`).
+ */
+ val isAutoConfirmEnabled: StateFlow<Boolean> =
+ combine(repository.isAutoConfirmFeatureEnabled, isThrottled) { featureEnabled, isThrottled
+ ->
+ // Disable auto-confirm during throttling.
+ featureEnabled && !isThrottled
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
/** The length of the hinted PIN, or `null` if pin length hint should not be shown. */
val hintedPinLength: StateFlow<Int?> =
- repository.isAutoConfirmEnabled
+ isAutoConfirmEnabled
.map { isAutoConfirmEnabled ->
repository.getPinLength().takeIf {
isAutoConfirmEnabled && it == repository.hintedPinLength
@@ -119,12 +135,19 @@ constructor(
initialValue = null,
)
- /** Whether the auto confirm feature is enabled for the currently-selected user. */
- val isAutoConfirmEnabled: StateFlow<Boolean> = repository.isAutoConfirmEnabled
-
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
+ /**
+ * Emits the outcome (successful or unsuccessful) whenever a PIN/Pattern/Password security
+ * challenge is attempted by the user in order to unlock the device.
+ */
+ val authenticationChallengeResult: SharedFlow<Boolean> =
+ repository.authenticationChallengeResult
+
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> = repository.isPinEnhancedPrivacyEnabled
+
private var throttlingCountdownJob: Job? = null
init {
@@ -147,17 +170,8 @@ constructor(
* The flow should be used for code that wishes to stay up-to-date its logic as the
* authentication changes over time and this method should be used for simple code that only
* needs to check the current value.
- *
- * Note: this layer adds the synthetic authentication method of "swipe" which is special. When
- * the current authentication method is "swipe", the user does not need to complete any
- * authentication challenge to unlock the device; they just need to dismiss the lockscreen to
- * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains
- * `true` even when the lockscreen is showing and still needs to be dismissed by the user to
- * proceed.
*/
- suspend fun getAuthenticationMethod(): DomainLayerAuthenticationMethodModel {
- return repository.getAuthenticationMethod().toDomainLayer()
- }
+ suspend fun getAuthenticationMethod() = repository.getAuthenticationMethod()
/**
* Attempts to authenticate the user and unlock the device.
@@ -186,14 +200,13 @@ constructor(
// We're being throttled, the UI layer should not have called this; skip the
// attempt.
isThrottled.value -> true
- // The pattern is too short; skip the attempt.
- authMethod == DomainLayerAuthenticationMethodModel.Pattern &&
- input.size < repository.minPatternLength -> true
+ // The input is too short; skip the attempt.
+ input.isTooShort(authMethod) -> true
// Auto-confirm attempt when the feature is not enabled; skip the attempt.
tryAutoConfirm && !isAutoConfirmEnabled.value -> true
// Auto-confirm should skip the attempt if the pin entered is too short.
tryAutoConfirm &&
- authMethod == DomainLayerAuthenticationMethodModel.Pin &&
+ authMethod == AuthenticationMethodModel.Pin &&
input.size < repository.getPinLength() -> true
else -> false
}
@@ -233,6 +246,14 @@ constructor(
}
}
+ private fun List<Any>.isTooShort(authMethod: AuthenticationMethodModel): Boolean {
+ return when (authMethod) {
+ AuthenticationMethodModel.Pattern -> size < repository.minPatternLength
+ AuthenticationMethodModel.Password -> size < repository.minPasswordLength
+ else -> false
+ }
+ }
+
/** Starts refreshing the throttling state every second. */
private suspend fun startThrottlingCountdown() {
cancelThrottlingCountdown()
@@ -279,15 +300,15 @@ constructor(
}
}
- private fun DomainLayerAuthenticationMethodModel.createCredential(
+ private fun AuthenticationMethodModel.createCredential(
input: List<Any>
): LockscreenCredential? {
return when (this) {
- is DomainLayerAuthenticationMethodModel.Pin ->
+ is AuthenticationMethodModel.Pin ->
LockscreenCredential.createPin(input.joinToString(""))
- is DomainLayerAuthenticationMethodModel.Password ->
+ is AuthenticationMethodModel.Password ->
LockscreenCredential.createPassword(input.joinToString(""))
- is DomainLayerAuthenticationMethodModel.Pattern ->
+ is AuthenticationMethodModel.Pattern ->
LockscreenCredential.createPattern(
input
.map { it as AuthenticationPatternCoordinate }
@@ -297,23 +318,6 @@ constructor(
}
}
- private suspend fun DataLayerAuthenticationMethodModel.toDomainLayer():
- DomainLayerAuthenticationMethodModel {
- return when (this) {
- is DataLayerAuthenticationMethodModel.None ->
- if (deviceEntryRepository.isInsecureLockscreenEnabled()) {
- DomainLayerAuthenticationMethodModel.Swipe
- } else {
- DomainLayerAuthenticationMethodModel.None
- }
- is DataLayerAuthenticationMethodModel.Pin -> DomainLayerAuthenticationMethodModel.Pin
- is DataLayerAuthenticationMethodModel.Password ->
- DomainLayerAuthenticationMethodModel.Password
- is DataLayerAuthenticationMethodModel.Pattern ->
- DomainLayerAuthenticationMethodModel.Pattern
- }
- }
-
companion object {
const val TAG = "AuthenticationInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index d7e6099a8908..3552a1957f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.authentication.domain.model
+package com.android.systemui.authentication.shared.model
/** Enumerates all known authentication methods. */
sealed class AuthenticationMethodModel(
@@ -26,15 +26,17 @@ sealed class AuthenticationMethodModel(
*/
open val isSecure: Boolean,
) {
- /** There is no authentication method on the device. We shouldn't even show the lock screen. */
+ /**
+ * Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
+ * screen can be swiped away when displayed.
+ */
object None : AuthenticationMethodModel(isSecure = false)
- /** The most basic authentication method. The lock screen can be swiped away when displayed. */
- object Swipe : AuthenticationMethodModel(isSecure = false)
-
object Pin : AuthenticationMethodModel(isSecure = true)
object Password : AuthenticationMethodModel(isSecure = true)
object Pattern : AuthenticationMethodModel(isSecure = true)
+
+ object Sim : AuthenticationMethodModel(isSecure = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 57e252dd9929..8fe42b536b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -100,7 +100,6 @@ import javax.inject.Inject;
import javax.inject.Provider;
import kotlin.Unit;
-
import kotlinx.coroutines.CoroutineScope;
/**
@@ -1099,6 +1098,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
// TODO(b/141025588): Create separate methods for handling hard and soft errors.
final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
|| error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
+ || error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL
|| isCameraPrivacyEnabled);
if (mCurrentDialog != null) {
if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index f94f8c594aa6..634531215002 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -31,11 +31,11 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -191,7 +191,7 @@ class AuthRippleController @Inject constructor(
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
- if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (!lightRevealMigration()) {
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim.revealAmount = 0f
@@ -210,7 +210,7 @@ class AuthRippleController @Inject constructor(
}
override fun onKeyguardFadingAwayChanged() {
- if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 8f61dbfbdd10..91cee9e51a93 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -62,6 +62,7 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
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.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
import com.android.systemui.util.boundsOnScreen
@@ -190,7 +191,10 @@ constructor(
}
private fun listenForAlternateBouncerVisibility() {
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController")
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController")
+ }
+
scope.launch {
alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 417593787ee9..e15538b88d8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -52,6 +52,7 @@ import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -78,11 +79,12 @@ 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.Main;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
@@ -149,7 +151,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@NonNull private final VibratorHelper mVibrator;
- @NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -170,6 +171,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final SelectedUserInteractor mSelectedUserInteractor;
@NonNull private final FpsUnlockTracker mFpsUnlockTracker;
private final boolean mIgnoreRefreshRate;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -279,12 +281,11 @@ public class UdfpsController implements DozeReceiver, Dumpable {
fromUdfpsView
),
mActivityLaunchAnimator,
- mFeatureFlags,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels,
- mSelectedUserInteractor
+ mKeyguardTransitionInteractor,
+ mSelectedUserInteractor
)));
}
@@ -316,10 +317,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return;
}
mAcquiredReceived = true;
- final UdfpsView view = mOverlay.getOverlayView();
- if (view != null && isOptical()) {
- unconfigureDisplay(view);
- }
+ final View view = mOverlay.getTouchOverlay();
+ unconfigureDisplay(view);
tryAodSendFingerUp();
});
}
@@ -337,7 +336,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (mOverlay == null || mOverlay.isHiding()) {
return;
}
- mOverlay.getOverlayView().setDebugMessage(message);
+ if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+ ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
+ }
});
}
@@ -504,6 +505,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
&& !mAlternateBouncerInteractor.isVisibleState())
|| mPrimaryBouncerInteractor.isInTransit()) {
+ Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
return false;
}
if (event.getAction() == MotionEvent.ACTION_DOWN
@@ -561,7 +563,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
mAttemptedToDismissKeyguard = false;
onFingerUp(requestId,
- mOverlay.getOverlayView(),
+ mOverlay.getTouchOverlay(),
data.getPointerId(),
data.getX(),
data.getY(),
@@ -595,7 +597,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (shouldPilfer && !mPointerPilfered
&& getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
mInputManager.pilferPointers(
- mOverlay.getOverlayView().getViewRootImpl().getInputToken());
+ mOverlay.getTouchOverlay().getViewRootImpl().getInputToken());
mPointerPilfered = true;
}
@@ -603,9 +605,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
private boolean shouldTryToDismissKeyguard() {
- return mOverlay != null
- && mOverlay.getAnimationViewController()
- instanceof UdfpsKeyguardViewControllerAdapter
+ boolean onKeyguard = false;
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ onKeyguard = mKeyguardStateController.isShowing();
+ } else {
+ onKeyguard = mOverlay != null
+ && mOverlay.getAnimationViewController()
+ instanceof UdfpsKeyguardViewControllerAdapter;
+ }
+ return onKeyguard
&& mKeyguardStateController.canDismissLockScreen()
&& !mAttemptedToDismissKeyguard;
}
@@ -621,7 +629,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull FeatureFlags featureFlags,
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@@ -649,7 +656,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
@NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider,
@NonNull SelectedUserInteractor selectedUserInteractor,
- @NonNull FpsUnlockTracker fpsUnlockTracker) {
+ @NonNull FpsUnlockTracker fpsUnlockTracker,
+ @NonNull KeyguardTransitionInteractor keyguardTransitionInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -667,7 +675,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mDumpManager = dumpManager;
mDialogManager = dialogManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mFeatureFlags = featureFlags;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
@@ -695,6 +702,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mSelectedUserInteractor = selectedUserInteractor;
mFpsUnlockTracker = fpsUnlockTracker;
mFpsUnlockTracker.startTracking();
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mTouchProcessor = singlePointerTouchProcessor;
mSessionTracker = sessionTracker;
@@ -733,9 +741,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@VisibleForTesting
public void playStartHaptic() {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (mOverlay != null && mOverlay.getOverlayView() != null) {
+ if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
mVibrator.performHapticFeedback(
- mOverlay.getOverlayView(),
+ mOverlay.getTouchOverlay(),
HapticFeedbackConstants.CONTEXT_CLICK
);
} else {
@@ -747,10 +755,11 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@Override
public void dozeTimeTick() {
- if (mOverlay != null) {
- final UdfpsView view = mOverlay.getOverlayView();
+ if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode();
+ final View view = mOverlay.getTouchOverlay();
if (view != null) {
- view.dozeTimeTick();
+ ((UdfpsView) view).dozeTimeTick();
}
}
}
@@ -793,7 +802,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (mOverlay != null) {
// Reset the controller back to its starting state.
- final UdfpsView oldView = mOverlay.getOverlayView();
+ final View oldView = mOverlay.getTouchOverlay();
if (oldView != null) {
onFingerUp(mOverlay.getRequestId(), oldView);
}
@@ -809,9 +818,21 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
- private void unconfigureDisplay(@NonNull UdfpsView view) {
- if (view.isDisplayConfigured()) {
- view.unconfigureDisplay();
+ private void unconfigureDisplay(View view) {
+ if (!isOptical()) {
+ return;
+ }
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ if (mUdfpsDisplayMode != null) {
+ mUdfpsDisplayMode.disable(null); // beverlt
+ }
+ } else {
+ if (view != null) {
+ UdfpsView udfpsView = (UdfpsView) view;
+ if (udfpsView.isDisplayConfigured()) {
+ udfpsView.unconfigureDisplay();
+ }
+ }
}
}
@@ -833,10 +854,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
mKeyguardViewManager.showPrimaryBouncer(true);
- // play the same haptic as the LockIconViewController longpress
- if (mOverlay != null && mOverlay.getOverlayView() != null) {
+ // play the same haptic as the DeviceEntryIcon longpress
+ if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
mVibrator.performHapticFeedback(
- mOverlay.getOverlayView(),
+ mOverlay.getTouchOverlay(),
UdfpsController.LONG_PRESS
);
} else {
@@ -905,8 +926,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return;
}
cancelAodSendFingerUpAction();
- if (mOverlay != null && mOverlay.getOverlayView() != null) {
- onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView());
+ if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
+ onFingerUp(mOverlay.getRequestId(), mOverlay.getTouchOverlay());
}
}
@@ -1000,12 +1021,17 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
minor, major, orientation, time, gestureStart, isAod);
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
- final UdfpsView view = mOverlay.getOverlayView();
+
+ final View view = mOverlay.getTouchOverlay();
if (view != null && isOptical()) {
if (mIgnoreRefreshRate) {
dispatchOnUiReady(requestId);
} else {
- view.configureDisplay(() -> dispatchOnUiReady(requestId));
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
+ } else {
+ ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
+ }
}
}
@@ -1014,7 +1040,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
}
- private void onFingerUp(long requestId, @NonNull UdfpsView view) {
+ private void onFingerUp(long requestId, @NonNull View view) {
onFingerUp(
requestId,
view,
@@ -1031,7 +1057,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
private void onFingerUp(
long requestId,
- @NonNull UdfpsView view,
+ View view,
int pointerId,
float x,
float y,
@@ -1052,9 +1078,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
}
mOnFingerDown = false;
- if (isOptical()) {
- unconfigureDisplay(view);
- }
+ unconfigureDisplay(view);
cancelAodSendFingerUpAction();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 934f9f919d5d..2d54f7ac8e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,13 +46,13 @@ import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
+import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -63,7 +63,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import javax.inject.Provider
private const val TAG = "UdfpsControllerOverlay"
@@ -97,17 +96,29 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
private val controllerCallback: IUdfpsOverlayControllerCallback,
private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
private val activityLaunchAnimator: ActivityLaunchAnimator,
- private val featureFlags: FeatureFlags,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
+ private val transitionInteractor: KeyguardTransitionInteractor,
private val selectedUserInteractor: SelectedUserInteractor,
) {
- /** The view, when [isShowing], or null. */
- var overlayView: UdfpsView? = null
+ private var overlayViewLegacy: UdfpsView? = null
private set
+ private var overlayTouchView: UdfpsTouchOverlay? = null
+
+ /**
+ * Get the current UDFPS overlay touch view which is a different View depending on whether
+ * the DeviceEntryUdfpsRefactor flag is enabled or not.
+ * @return The view, when [isShowing], else null
+ */
+ fun getTouchOverlay(): View? {
+ return if (DeviceEntryUdfpsRefactor.isEnabled) {
+ overlayTouchView
+ } else {
+ overlayViewLegacy
+ }
+ }
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
private var sensorBounds: Rect = Rect()
@@ -133,15 +144,15 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
/** If the overlay is currently showing. */
val isShowing: Boolean
- get() = overlayView != null
+ get() = getTouchOverlay() != null
/** Opposite of [isShowing]. */
val isHiding: Boolean
- get() = overlayView == null
+ get() = getTouchOverlay() == null
/** The animation controller if the overlay [isShowing]. */
val animationViewController: UdfpsAnimationViewController<*>?
- get() = overlayView?.animationViewController
+ get() = overlayViewLegacy?.animationViewController
private var touchExplorationEnabled = false
@@ -159,28 +170,48 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
/** Show the overlay or return false and do nothing if it is already showing. */
@SuppressLint("ClickableViewAccessibility")
fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
- if (overlayView == null) {
+ if (getTouchOverlay() == null) {
overlayParams = params
sensorBounds = Rect(params.sensorBounds)
try {
- overlayView = (inflater.inflate(
- R.layout.udfps_view, null, false
- ) as UdfpsView).apply {
- overlayParams = params
- setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
- val animation = inflateUdfpsAnimation(this, controller)
- if (animation != null) {
- animation.init()
- animationViewController = animation
- }
- // This view overlaps the sensor area
- // prevent it from being selectable during a11y
- if (requestReason.isImportantForAccessibility()) {
- importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ overlayTouchView = (inflater.inflate(
+ R.layout.udfps_touch_overlay, null, false
+ ) as UdfpsTouchOverlay).apply {
+ // This view overlaps the sensor area
+ // prevent it from being selectable during a11y
+ if (requestReason.isImportantForAccessibility()) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
+ windowManager.addView(this, coreLayoutParams.updateDimensions(null))
}
+ // TODO (b/305234447): Bind view model to UdfpsTouchOverlay here to control
+ // the visibility (sometimes, even if UDFPS is running, the UDFPS UI can be
+ // obscured and we don't want to accept touches. ie: for enrollment don't accept
+ // touches when the shade is expanded and for keyguard: don't accept touches
+ // depending on the keyguard & shade state
+ } else {
+ overlayViewLegacy = (inflater.inflate(
+ R.layout.udfps_view, null, false
+ ) as UdfpsView).apply {
+ overlayParams = params
+ setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
+ val animation = inflateUdfpsAnimation(this, controller)
+ if (animation != null) {
+ animation.init()
+ animationViewController = animation
+ }
+ // This view overlaps the sensor area
+ // prevent it from being selectable during a11y
+ if (requestReason.isImportantForAccessibility()) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
- windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
- sensorRect = sensorBounds
+ windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
+ sensorRect = sensorBounds
+ }
+ }
+ getTouchOverlay()?.apply {
touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
overlayTouchListener = TouchExplorationStateChangeListener {
if (accessibilityManager.isTouchExplorationEnabled) {
@@ -194,7 +225,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
}
}
accessibilityManager.addTouchExplorationStateChangeListener(
- overlayTouchListener!!
+ overlayTouchListener!!
)
overlayTouchListener?.onTouchExplorationStateChanged(true)
}
@@ -212,6 +243,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
view: UdfpsView,
controller: UdfpsController
): UdfpsAnimationViewController<*>? {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode()
+
val isEnrollment = when (requestReason) {
REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
else -> false
@@ -238,39 +271,27 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
)
}
REASON_AUTH_KEYGUARD -> {
- if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
- // note: empty controller, currently shows no visual affordance
- // instead SysUI will show the fingerprint icon in its DeviceEntryIconView
- UdfpsBpViewController(
- view.addUdfpsView(R.layout.udfps_bp_view),
- statusBarStateController,
- primaryBouncerInteractor,
- dialogManager,
- dumpManager
- )
- } else {
- UdfpsKeyguardViewControllerLegacy(
- view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
- updateSensorLocation(sensorBounds)
- },
- statusBarStateController,
- statusBarKeyguardViewManager,
- keyguardUpdateMonitor,
- dumpManager,
- transitionController,
- configurationController,
- keyguardStateController,
- unlockedScreenOffAnimationController,
- dialogManager,
- controller,
- activityLaunchAnimator,
- featureFlags,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- udfpsKeyguardAccessibilityDelegate,
- selectedUserInteractor,
- )
- }
+ UdfpsKeyguardViewControllerLegacy(
+ view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
+ updateSensorLocation(sensorBounds)
+ },
+ statusBarStateController,
+ statusBarKeyguardViewManager,
+ keyguardUpdateMonitor,
+ dumpManager,
+ transitionController,
+ configurationController,
+ keyguardStateController,
+ unlockedScreenOffAnimationController,
+ dialogManager,
+ controller,
+ activityLaunchAnimator,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ udfpsKeyguardAccessibilityDelegate,
+ selectedUserInteractor,
+ transitionInteractor,
+ )
}
REASON_AUTH_BP -> {
// note: empty controller, currently shows no visual affordance
@@ -303,19 +324,26 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
fun hide(): Boolean {
val wasShowing = isShowing
- overlayView?.apply {
+ overlayViewLegacy?.apply {
if (isDisplayConfigured) {
unconfigureDisplay()
}
+ animationViewController = null
+ }
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ udfpsDisplayModeProvider.disable(null)
+ }
+ getTouchOverlay()?.apply {
windowManager.removeView(this)
setOnTouchListener(null)
setOnHoverListener(null)
- animationViewController = null
overlayTouchListener?.let {
accessibilityManager.removeTouchExplorationStateChangeListener(it)
}
}
- overlayView = null
+
+ overlayViewLegacy = null
+ overlayTouchView = null
overlayTouchListener = null
return wasShowing
@@ -393,7 +421,14 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
}
private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
- if (animation !is UdfpsKeyguardViewControllerAdapter) {
+ val keyguardNotShowing =
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ !keyguardStateController.isShowing
+ } else {
+ animation !is UdfpsKeyguardViewControllerAdapter
+ }
+
+ if (keyguardNotShowing) {
// always rotate view if we're not on the keyguard
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index d7df0e585dcf..35c3ded9e984 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -16,7 +16,6 @@
package com.android.systemui.biometrics
-import android.animation.ValueAnimator
import android.content.res.Configuration
import android.util.MathUtils
import android.view.View
@@ -27,17 +26,17 @@ import com.android.app.animation.Interpolators
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_UNLOCKED_SCREEN_OFF
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI
@@ -48,10 +47,14 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.io.PrintWriter
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
/** Class that coordinates non-HBM animations during keyguard authentication. */
+@ExperimentalCoroutinesApi
open class UdfpsKeyguardViewControllerLegacy(
private val view: UdfpsKeyguardViewLegacy,
statusBarStateController: StatusBarStateController,
@@ -65,11 +68,11 @@ open class UdfpsKeyguardViewControllerLegacy(
systemUIDialogManager: SystemUIDialogManager,
private val udfpsController: UdfpsController,
private val activityLaunchAnimator: ActivityLaunchAnimator,
- featureFlags: FeatureFlags,
primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
private val selectedUserInteractor: SelectedUserInteractor,
+ private val transitionInteractor: KeyguardTransitionInteractor,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
@@ -91,44 +94,10 @@ open class UdfpsKeyguardViewControllerLegacy(
private var launchTransitionFadingAway = false
private var isLaunchingActivity = false
private var activityLaunchProgress = 0f
- private val unlockedScreenOffDozeAnimator =
- ValueAnimator.ofFloat(0f, 1f).apply {
- duration = StackStateAnimator.ANIMATION_DURATION_STANDARD.toLong()
- interpolator = Interpolators.ALPHA_IN
- addUpdateListener { animation ->
- view.onDozeAmountChanged(
- animation.animatedFraction,
- animation.animatedValue as Float,
- UdfpsKeyguardViewLegacy.ANIMATION_UNLOCKED_SCREEN_OFF
- )
- }
- }
private var inputBouncerExpansion = 0f
private val stateListener: StatusBarStateController.StateListener =
object : StatusBarStateController.StateListener {
- override fun onDozeAmountChanged(linear: Float, eased: Float) {
- if (lastDozeAmount < linear) {
- showUdfpsBouncer(false)
- }
- unlockedScreenOffDozeAnimator.cancel()
- val animatingFromUnlockedScreenOff =
- unlockedScreenOffAnimationController.isAnimationPlaying()
- if (animatingFromUnlockedScreenOff && linear != 0f) {
- // we manually animate the fade in of the UDFPS icon since the unlocked
- // screen off animation prevents the doze amounts to be incrementally eased in
- unlockedScreenOffDozeAnimator.start()
- } else {
- view.onDozeAmountChanged(
- linear,
- eased,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
- )
- }
- lastDozeAmount = linear
- updatePauseAuth()
- }
-
override fun onStateChanged(statusBarState: Int) {
this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState
updateAlpha()
@@ -215,6 +184,7 @@ open class UdfpsKeyguardViewControllerLegacy(
}
init {
+ com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
view.repeatWhenAttached {
// repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
// can make the view not visible; and we still want to listen for events
@@ -222,11 +192,39 @@ open class UdfpsKeyguardViewControllerLegacy(
repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForBouncerExpansion(this)
listenForAlternateBouncerVisibility(this)
+ listenForGoneToAodTransition(this)
+ listenForLockscreenAodTransitions(this)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
+ return scope.launch {
+ transitionInteractor.goneToAodTransition.collect { transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ ANIMATION_UNLOCKED_SCREEN_OFF,
+ )
}
}
}
@VisibleForTesting
+ suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
+ return scope.launch {
+ transitionInteractor.dozeAmountTransition.collect { transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+ )
+ }
+ }
+ }
+
+ @VisibleForTesting
override suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
return scope.launch {
primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
index 95e3a76c11d8..f4ed8cef7bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
@@ -73,9 +73,6 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
// AOD anti-burn-in offsets
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
- private float mBurnInOffsetX;
- private float mBurnInOffsetY;
- private float mBurnInProgress;
private float mInterpolatedDarkAmount;
private int mAnimationType = ANIMATION_NONE;
private boolean mFullyInflated;
@@ -138,20 +135,22 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
// AoD-burn in location, else we need to translate the icon from LS => AoD.
final float darkAmountForAnimation = mAnimationType == ANIMATION_UNLOCKED_SCREEN_OFF
? 1f : mInterpolatedDarkAmount;
- mBurnInOffsetX = MathUtils.lerp(0f,
+ final float burnInOffsetX = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
- mMaxBurnInOffsetX, darkAmountForAnimation);
- mBurnInOffsetY = MathUtils.lerp(0f,
+ final float burnInOffsetY = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- mMaxBurnInOffsetY, darkAmountForAnimation);
- mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), darkAmountForAnimation);
+ final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(),
+ darkAmountForAnimation);
if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) {
- mLockScreenFp.setTranslationX(mBurnInOffsetX);
- mLockScreenFp.setTranslationY(mBurnInOffsetY);
+ mLockScreenFp.setTranslationX(burnInOffsetX);
+ mLockScreenFp.setTranslationY(burnInOffsetY);
mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
} else if (darkAmountForAnimation == 0f) {
+ // we're on the lockscreen and should use mAlpha (changes based on shade expansion)
mLockScreenFp.setTranslationX(0);
mLockScreenFp.setTranslationY(0);
mBgProtection.setAlpha(mAlpha / 255f);
@@ -162,9 +161,9 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
}
mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
- mAodFp.setTranslationX(mBurnInOffsetX);
- mAodFp.setTranslationY(mBurnInOffsetY);
- mAodFp.setProgress(mBurnInProgress);
+ mAodFp.setTranslationX(burnInOffsetX);
+ mAodFp.setTranslationY(burnInOffsetY);
+ mAodFp.setProgress(burnInProgress);
mAodFp.setAlpha(mInterpolatedDarkAmount);
// done animating
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt
index 039078eb1f21..2484c339a1d4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/view/UdfpsTouchOverlay.kt
@@ -12,20 +12,15 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
*/
-
-package com.android.systemui.communal.ui.view
+package com.android.systemui.biometrics.ui.view
import android.content.Context
import android.util.AttributeSet
-import android.widget.LinearLayout
-import com.android.systemui.res.R
+import android.widget.FrameLayout
-/** Wraps around a widget rendered in communal mode. */
-class CommunalWidgetWrapper(context: Context, attrs: AttributeSet? = null) :
- LinearLayout(context, attrs) {
- init {
- id = R.id.communal_widget_wrapper
- }
-}
+/**
+ * A translucent (not visible to the user) view that receives touches to send to FingerprintManager
+ * for fingerprint authentication.
+ */
+class UdfpsTouchOverlay(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimBouncerModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimBouncerModel.kt
new file mode 100644
index 000000000000..5fc510154681
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimBouncerModel.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.bouncer.data.model
+
+/** Represents the locked sim card in the Bouncer. */
+data class SimBouncerModel(val isSimPukLocked: Boolean, val subscriptionId: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimPukInputModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimPukInputModel.kt
new file mode 100644
index 000000000000..3cd88d6044d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/model/SimPukInputModel.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.bouncer.data.model
+
+/**
+ * Represents the user flow for unlocking a PUK locked sim card.
+ *
+ * After entering the puk code, we need to enter and confirm a new pin code for the sim card.
+ */
+data class SimPukInputModel(
+ val enteredSimPuk: String? = null,
+ val enteredSimPin: String? = null,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryModule.kt
new file mode 100644
index 000000000000..ff6321cad670
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryModule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.bouncer.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface BouncerRepositoryModule {
+ @Binds
+ fun provideSimRepository(simRepositoryImpl: SimBouncerRepositoryImpl): SimBouncerRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index c0b21534c5f3..1e0e16c5472f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -22,6 +22,7 @@ import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EX
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.log.dagger.BouncerTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
@@ -208,6 +209,7 @@ constructor(
}
override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode()
_alternateBouncerUIAvailable.value = isAvailable
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
new file mode 100644
index 000000000000..269878b43dab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
@@ -0,0 +1,218 @@
+/*
+ * 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.bouncer.data.repository
+
+import android.annotation.SuppressLint
+import android.content.IntentFilter
+import android.content.res.Resources
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.bouncer.data.model.SimBouncerModel
+import com.android.systemui.bouncer.data.model.SimPukInputModel
+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.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+/** Handles data layer logic for locked sim cards. */
+interface SimBouncerRepository {
+ /** The subscription id of the current locked sim card. */
+ val subscriptionId: StateFlow<Int>
+ /** The active subscription of the current subscription id. */
+ val activeSubscriptionInfo: StateFlow<SubscriptionInfo?>
+ /**
+ * Determines if current sim card is an esim and is locked.
+ *
+ * A null value indicates that we do not know if we are esim locked or not.
+ */
+ val isLockedEsim: StateFlow<Boolean?>
+ /**
+ * Determines whether the current sim is locked requiring a PUK (Personal Unlocking Key) code.
+ */
+ val isSimPukLocked: StateFlow<Boolean>
+ /**
+ * The error message that should be displayed in an alert dialog.
+ *
+ * A null value indicates that the error dialog is not showing.
+ */
+ val errorDialogMessage: StateFlow<String?>
+ /** The state of the user flow on the SimPuk screen. */
+ val simPukInputModel: SimPukInputModel
+ /** Sets the state of the user flow on the SimPuk screen. */
+ fun setSimPukUserInput(enteredSimPuk: String? = null, enteredSimPin: String? = null)
+ /**
+ * Sets the error message when failing sim verification.
+ *
+ * A null value indicates that there is no error message to show.
+ */
+ fun setSimVerificationErrorMessage(msg: String?)
+}
+
+@SysUISingleton
+class SimBouncerRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Main resources: Resources,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val subscriptionManager: SubscriptionManagerProxy,
+ broadcastDispatcher: BroadcastDispatcher,
+ euiccManager: EuiccManager,
+) : SimBouncerRepository {
+ private val isPukScreenAvailable: Boolean =
+ resources.getBoolean(com.android.internal.R.bool.config_enable_puk_unlock_screen)
+
+ private val simBouncerModel: Flow<SimBouncerModel?> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
+ trySend(Unit)
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .map {
+ // Check to see if there is a locked sim puk card.
+ val pukLockedSubId =
+ withContext(backgroundDispatcher) {
+ keyguardUpdateMonitor.getNextSubIdForState(
+ TelephonyManager.SIM_STATE_PUK_REQUIRED
+ )
+ }
+ if (
+ isPukScreenAvailable &&
+ subscriptionManager.isValidSubscriptionId(pukLockedSubId)
+ ) {
+ return@map (SimBouncerModel(isSimPukLocked = true, pukLockedSubId))
+ }
+
+ // If there is no locked sim puk card, check to see if there is a locked sim card.
+ val pinLockedSubId =
+ withContext(backgroundDispatcher) {
+ keyguardUpdateMonitor.getNextSubIdForState(
+ TelephonyManager.SIM_STATE_PIN_REQUIRED
+ )
+ }
+ if (subscriptionManager.isValidSubscriptionId(pinLockedSubId)) {
+ return@map SimBouncerModel(isSimPukLocked = false, pinLockedSubId)
+ }
+
+ return@map null // There is no locked sim.
+ }
+
+ override val subscriptionId: StateFlow<Int> =
+ simBouncerModel
+ .map { state -> state?.subscriptionId ?: INVALID_SUBSCRIPTION_ID }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = INVALID_SUBSCRIPTION_ID,
+ )
+
+ @SuppressLint("MissingPermission")
+ override val activeSubscriptionInfo: StateFlow<SubscriptionInfo?> =
+ subscriptionId
+ .map {
+ withContext(backgroundDispatcher) {
+ subscriptionManager.getActiveSubscriptionInfo(it)
+ }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = null,
+ )
+
+ @SuppressLint("MissingPermission")
+ override val isLockedEsim: StateFlow<Boolean?> =
+ activeSubscriptionInfo
+ .map { info -> info?.let { euiccManager.isEnabled && info.isEmbedded } }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = null,
+ )
+
+ override val isSimPukLocked: StateFlow<Boolean> =
+ simBouncerModel
+ .map { it?.isSimPukLocked == true }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ private val disableEsimErrorMessage: Flow<String?> =
+ broadcastDispatcher.broadcastFlow(filter = IntentFilter(ACTION_DISABLE_ESIM)) { _, receiver
+ ->
+ if (receiver.resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ resources.getString(R.string.error_disable_esim_msg)
+ } else {
+ null
+ }
+ }
+
+ private val simVerificationErrorMessage: MutableStateFlow<String?> = MutableStateFlow(null)
+
+ override val errorDialogMessage: StateFlow<String?> =
+ merge(disableEsimErrorMessage, simVerificationErrorMessage)
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null,
+ )
+
+ private var _simPukInputModel: SimPukInputModel = SimPukInputModel()
+ override val simPukInputModel: SimPukInputModel
+ get() = _simPukInputModel
+
+ override fun setSimPukUserInput(enteredSimPuk: String?, enteredSimPin: String?) {
+ _simPukInputModel = SimPukInputModel(enteredSimPuk, enteredSimPin)
+ }
+
+ override fun setSimVerificationErrorMessage(msg: String?) {
+ simVerificationErrorMessage.value = msg
+ }
+
+ companion object {
+ const val ACTION_DISABLE_ESIM = "com.android.keyguard.disable_esim"
+ const val INVALID_SUBSCRIPTION_ID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 9a7fec1daae0..a7211007e5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -17,14 +17,23 @@
package com.android.systemui.bouncer.domain.interactor
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */
@SysUISingleton
@@ -34,13 +43,29 @@ constructor(
private val statusBarStateController: StatusBarStateController,
private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository,
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
private val biometricSettingsRepository: BiometricSettingsRepository,
private val systemClock: SystemClock,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ @Application scope: CoroutineScope,
) {
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
+ private val alternateBouncerSupported: StateFlow<Boolean> =
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ fingerprintPropertyRepository.sensorType
+ .map { sensorType ->
+ sensorType.isUdfps() || sensorType == FingerprintSensorType.POWER_BUTTON
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+ } else {
+ bouncerRepository.alternateBouncerUIAvailable
+ }
/**
* Sets the correct bouncer states to show the alternate bouncer if it can show.
@@ -71,6 +96,7 @@ constructor(
}
fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) {
+ DeviceEntryUdfpsRefactor.assertInLegacyMode()
if (isAvailable) {
alternateBouncerUiAvailableFromSource.add(token)
} else {
@@ -82,7 +108,7 @@ constructor(
}
fun canShowAlternateBouncerForFingerprint(): Boolean {
- return bouncerRepository.alternateBouncerUIAvailable.value &&
+ return alternateBouncerSupported.value &&
biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value &&
!keyguardUpdateMonitor.isFingerprintLockedOut &&
!keyguardStateController.isUnlocked &&
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b9a913ea866f..b598631c3b57 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -19,24 +19,24 @@ package com.android.systemui.bouncer.domain.interactor
import android.content.Context
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
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.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
-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.scene.shared.model.SceneModel
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -51,11 +51,12 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
private val repository: BouncerRepository,
- private val deviceEntryInteractor: DeviceEntryInteractor,
private val authenticationInteractor: AuthenticationInteractor,
- private val sceneInteractor: SceneInteractor,
+ private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
flags: SceneContainerFlags,
private val falsingInteractor: FalsingInteractor,
+ private val powerInteractor: PowerInteractor,
+ private val simBouncerInteractor: SimBouncerInteractor,
) {
/** The user-facing message to show in the bouncer. */
@@ -96,10 +97,18 @@ constructor(
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ authenticationInteractor.isPinEnhancedPrivacyEnabled
+
/** Whether the user switcher should be displayed within the bouncer UI on large screens. */
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
+ private val _onImeHidden = MutableSharedFlow<Unit>()
+ /** Provide the onImeHidden events from the bouncer */
+ val onImeHidden: SharedFlow<Unit> = _onImeHidden
+
init {
if (flags.isEnabled()) {
// Clear the message if moved from throttling to no-longer throttling.
@@ -124,6 +133,8 @@ constructor(
* user's pocket or by the user's face while holding their device up to their ear.
*/
fun onIntentionalUserInput() {
+ keyguardFaceAuthInteractor.onPrimaryBouncerUserInput()
+ powerInteractor.onUserTouch()
falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
}
@@ -141,30 +152,8 @@ constructor(
)
}
- /**
- * Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown.
- *
- * @param message An optional message to show to the user in the bouncer.
- */
- fun showOrUnlockDevice(
- message: String? = null,
- ) {
- applicationScope.launch {
- if (deviceEntryInteractor.isAuthenticationRequired()) {
- repository.setMessage(
- message ?: promptMessage(authenticationInteractor.getAuthenticationMethod())
- )
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Bouncer),
- loggingReason = "request to unlock device while authentication required",
- )
- } else {
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Gone),
- loggingReason = "request to unlock device while authentication isn't required",
- )
- }
- }
+ fun setMessage(message: String?) {
+ repository.setMessage(message)
}
/**
@@ -205,6 +194,12 @@ constructor(
if (input.isEmpty()) {
return AuthenticationResult.SKIPPED
}
+
+ if (authenticationInteractor.getAuthenticationMethod() == AuthenticationMethodModel.Sim) {
+ // We authenticate sim in SimInteractor
+ return AuthenticationResult.SKIPPED
+ }
+
// Switching to the application scope here since this method is often called from
// view-models, whose lifecycle (and thus scope) is shorter than this interactor.
// This allows the task to continue running properly even when the calling scope has been
@@ -212,17 +207,11 @@ constructor(
return applicationScope
.async {
val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm)
- when (authResult) {
- // Authentication succeeded.
- AuthenticationResult.SUCCEEDED ->
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Gone),
- loggingReason = "successful authentication",
- )
- // Authentication failed.
- AuthenticationResult.FAILED -> showErrorMessage()
- // Authentication skipped.
- AuthenticationResult.SKIPPED -> if (!tryAutoConfirm) showErrorMessage()
+ if (
+ authResult == AuthenticationResult.FAILED ||
+ (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm)
+ ) {
+ showErrorMessage()
}
authResult
}
@@ -242,20 +231,13 @@ constructor(
}
/** Notifies the interactor that the input method editor has been hidden. */
- fun onImeHidden() {
- // If the bouncer is showing, hide it and return to the lockscreen scene.
- if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
- return
- }
-
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Lockscreen),
- loggingReason = "IME hidden",
- )
+ suspend fun onImeHidden() {
+ _onImeHidden.emit(Unit)
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
+ is AuthenticationMethodModel.Sim -> simBouncerInteractor.getDefaultMessage()
is AuthenticationMethodModel.Pin ->
applicationContext.getString(R.string.keyguard_enter_your_pin)
is AuthenticationMethodModel.Password ->
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
index e398c930e86e..efa77926a423 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
@@ -19,6 +19,7 @@ package com.android.systemui.bouncer.domain.interactor
import android.content.Context
import android.content.Intent
import android.telecom.TelecomManager
+import android.telephony.euicc.EuiccManager
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -47,4 +48,9 @@ object BouncerInteractorModule {
): EmergencyAffordanceManager {
return EmergencyAffordanceManager(applicationContext)
}
+
+ @Provides
+ fun provideEuiccManager(@Application applicationContext: Context): EuiccManager {
+ return applicationContext.getSystemService(Context.EUICC_SERVICE) as EuiccManager
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
new file mode 100644
index 000000000000..99d1f1370f4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -0,0 +1,340 @@
+/*
+ * 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.bouncer.domain.interactor
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.res.Resources
+import android.os.UserHandle
+import android.telephony.PinResult
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import android.text.TextUtils
+import android.util.Log
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.bouncer.data.repository.SimBouncerRepository
+import com.android.systemui.bouncer.data.repository.SimBouncerRepositoryImpl
+import com.android.systemui.bouncer.data.repository.SimBouncerRepositoryImpl.Companion.ACTION_DISABLE_ESIM
+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.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.util.icuMessageFormat
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/** Handles domain layer logic for locked sim cards. */
+@SuppressLint("WrongConstant")
+@SysUISingleton
+class SimBouncerInteractor
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val repository: SimBouncerRepository,
+ private val telephonyManager: TelephonyManager,
+ @Main private val resources: Resources,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val euiccManager: EuiccManager,
+ // TODO(b/307977401): Replace this with `MobileConnectionsInteractor` when available.
+ mobileConnectionsRepository: MobileConnectionsRepository,
+) {
+ val subId: StateFlow<Int> = repository.subscriptionId
+ val isAnySimSecure: Flow<Boolean> = mobileConnectionsRepository.isAnySimSecure
+ val isLockedEsim: StateFlow<Boolean?> = repository.isLockedEsim
+ val errorDialogMessage: StateFlow<String?> = repository.errorDialogMessage
+
+ /** Returns the default message for the sim pin screen. */
+ fun getDefaultMessage(): String {
+ val isEsimLocked = repository.isLockedEsim.value ?: false
+ val isPuk: Boolean = repository.isSimPukLocked.value
+ val subscriptionId = repository.subscriptionId.value
+
+ if (subscriptionId == INVALID_SUBSCRIPTION_ID) {
+ Log.e(TAG, "Trying to get default message from unknown sub id")
+ return ""
+ }
+
+ var count = telephonyManager.activeModemCount
+ val info: SubscriptionInfo? = repository.activeSubscriptionInfo.value
+ val displayName = info?.displayName
+ var msg: String =
+ when {
+ count < 2 && isPuk -> resources.getString(R.string.kg_puk_enter_puk_hint)
+ count < 2 -> resources.getString(R.string.kg_sim_pin_instructions)
+ else -> {
+ when {
+ !TextUtils.isEmpty(displayName) && isPuk ->
+ resources.getString(R.string.kg_puk_enter_puk_hint_multi, displayName)
+ !TextUtils.isEmpty(displayName) ->
+ resources.getString(R.string.kg_sim_pin_instructions_multi, displayName)
+ isPuk -> resources.getString(R.string.kg_puk_enter_puk_hint)
+ else -> resources.getString(R.string.kg_sim_pin_instructions)
+ }
+ }
+ }
+
+ if (isEsimLocked) {
+ msg = resources.getString(R.string.kg_sim_lock_esim_instructions, msg)
+ }
+
+ return msg
+ }
+
+ /** Resets the user flow when the sim screen is puk locked. */
+ fun resetSimPukUserInput() {
+ repository.setSimPukUserInput()
+ // Force a garbage collection in an attempt to erase any sim pin or sim puk codes left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+
+ applicationScope.launch(backgroundDispatcher) {
+ delay(5000)
+ System.gc()
+ System.runFinalization()
+ System.gc()
+ }
+ }
+
+ /** Disables the locked esim card so user can bypass the sim pin screen. */
+ fun disableEsim() {
+ val activeSubscription = repository.activeSubscriptionInfo.value
+ if (activeSubscription == null) {
+ val subId = repository.subscriptionId.value
+ Log.e(TAG, "No active subscription with subscriptionId: $subId")
+ return
+ }
+ val intent = Intent(ACTION_DISABLE_ESIM)
+ intent.setPackage(applicationContext.packageName)
+ val callbackIntent =
+ PendingIntent.getBroadcastAsUser(
+ applicationContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE_UNAUDITED,
+ UserHandle.SYSTEM
+ )
+ applicationScope.launch(backgroundDispatcher) {
+ euiccManager.switchToSubscription(
+ INVALID_SUBSCRIPTION_ID,
+ activeSubscription.portIndex,
+ callbackIntent,
+ )
+ }
+ }
+
+ /** Update state when error dialog is dismissed by the user. */
+ fun onErrorDialogDismissed() {
+ repository.setSimVerificationErrorMessage(null)
+ }
+
+ /**
+ * Based on sim state, unlock the locked sim with the given credentials.
+ *
+ * @return Any message that should show associated with the provided input. Null means that no
+ * message needs to be shown.
+ */
+ suspend fun verifySim(input: List<Any>): String? {
+ if (repository.isSimPukLocked.value) {
+ return verifySimPuk(input.joinToString(separator = ""))
+ }
+
+ return verifySimPin(input.joinToString(separator = ""))
+ }
+
+ /**
+ * Verifies the input and unlocks the locked sim with a 4-8 digit pin code.
+ *
+ * @return Any message that should show associated with the provided input. Null means that no
+ * message needs to be shown.
+ */
+ private suspend fun verifySimPin(input: String): String? {
+ val subscriptionId = repository.subscriptionId.value
+ // A SIM PIN is 4 to 8 decimal digits according to
+ // GSM 02.17 version 5.0.1, Section 5.6 PIN Management
+ if (input.length < MIN_SIM_PIN_LENGTH || input.length > MAX_SIM_PIN_LENGTH) {
+ return resources.getString(R.string.kg_invalid_sim_pin_hint)
+ }
+ val result =
+ withContext(backgroundDispatcher) {
+ val telephonyManager: TelephonyManager =
+ telephonyManager.createForSubscriptionId(subscriptionId)
+ telephonyManager.supplyIccLockPin(input)
+ }
+ when (result.result) {
+ PinResult.PIN_RESULT_TYPE_SUCCESS ->
+ keyguardUpdateMonitor.reportSimUnlocked(subscriptionId)
+ PinResult.PIN_RESULT_TYPE_INCORRECT -> {
+ if (result.attemptsRemaining <= CRITICAL_NUM_OF_ATTEMPTS) {
+ // Show a dialog to display the remaining number of attempts to verify the sim
+ // pin to the user.
+ repository.setSimVerificationErrorMessage(
+ getPinPasswordErrorMessage(result.attemptsRemaining)
+ )
+ } else {
+ return getPinPasswordErrorMessage(result.attemptsRemaining)
+ }
+ }
+ }
+
+ return null
+ }
+
+ /**
+ * Verifies the input and unlocks the locked sim with a puk code instead of pin.
+ *
+ * This occurs after incorrectly verifying the sim pin multiple times.
+ *
+ * @return Any message that should show associated with the provided input. Null means that no
+ * message needs to be shown.
+ */
+ private suspend fun verifySimPuk(entry: String): String? {
+ val (enteredSimPuk, enteredSimPin) = repository.simPukInputModel
+ val subscriptionId: Int = repository.subscriptionId.value
+
+ // Stage 1: Enter the sim puk code of the sim card.
+ if (enteredSimPuk == null) {
+ if (entry.length >= MIN_SIM_PUK_LENGTH) {
+ repository.setSimPukUserInput(enteredSimPuk = entry)
+ return resources.getString(R.string.kg_puk_enter_pin_hint)
+ } else {
+ return resources.getString(R.string.kg_invalid_sim_puk_hint)
+ }
+ }
+
+ // Stage 2: Set a new sim pin to lock the sim card.
+ if (enteredSimPin == null) {
+ if (entry.length in MIN_SIM_PIN_LENGTH..MAX_SIM_PIN_LENGTH) {
+ repository.setSimPukUserInput(
+ enteredSimPuk = enteredSimPuk,
+ enteredSimPin = entry,
+ )
+ return resources.getString(R.string.kg_enter_confirm_pin_hint)
+ } else {
+ return resources.getString(R.string.kg_invalid_sim_pin_hint)
+ }
+ }
+
+ // Stage 3: Confirm the newly set sim pin.
+ if (repository.simPukInputModel.enteredSimPin != entry) {
+ // The entered sim pins do not match. Enter desired sim pin again to confirm.
+ repository.setSimVerificationErrorMessage(
+ resources.getString(R.string.kg_invalid_confirm_pin_hint)
+ )
+ repository.setSimPukUserInput(enteredSimPuk = enteredSimPuk)
+ return resources.getString(R.string.kg_puk_enter_pin_hint)
+ }
+
+ val result =
+ withContext(backgroundDispatcher) {
+ val telephonyManager = telephonyManager.createForSubscriptionId(subscriptionId)
+ telephonyManager.supplyIccLockPuk(enteredSimPuk, enteredSimPin)
+ }
+ resetSimPukUserInput()
+
+ when (result.result) {
+ PinResult.PIN_RESULT_TYPE_SUCCESS ->
+ keyguardUpdateMonitor.reportSimUnlocked(subscriptionId)
+ PinResult.PIN_RESULT_TYPE_INCORRECT ->
+ if (result.attemptsRemaining <= CRITICAL_NUM_OF_ATTEMPTS) {
+ // Show a dialog to display the remaining number of attempts to verify the sim
+ // puk to the user.
+ repository.setSimVerificationErrorMessage(
+ getPukPasswordErrorMessage(
+ result.attemptsRemaining,
+ isDefault = false,
+ isEsimLocked = repository.isLockedEsim.value == true
+ )
+ )
+ } else {
+ return getPukPasswordErrorMessage(
+ result.attemptsRemaining,
+ isDefault = false,
+ isEsimLocked = repository.isLockedEsim.value == true
+ )
+ }
+ else -> return resources.getString(R.string.kg_password_puk_failed)
+ }
+
+ return null
+ }
+
+ private fun getPinPasswordErrorMessage(attemptsRemaining: Int): String {
+ var displayMessage: String =
+ if (attemptsRemaining == 0) {
+ resources.getString(R.string.kg_password_wrong_pin_code_pukked)
+ } else if (attemptsRemaining > 0) {
+ val msgId = R.string.kg_password_default_pin_message
+ icuMessageFormat(resources, msgId, attemptsRemaining)
+ } else {
+ val msgId = R.string.kg_sim_pin_instructions
+ resources.getString(msgId)
+ }
+ if (repository.isLockedEsim.value == true) {
+ displayMessage =
+ resources.getString(R.string.kg_sim_lock_esim_instructions, displayMessage)
+ }
+ return displayMessage
+ }
+
+ private fun getPukPasswordErrorMessage(
+ attemptsRemaining: Int,
+ isDefault: Boolean,
+ isEsimLocked: Boolean,
+ ): String {
+ var displayMessage: String =
+ if (attemptsRemaining == 0) {
+ resources.getString(R.string.kg_password_wrong_puk_code_dead)
+ } else if (attemptsRemaining > 0) {
+ val msgId =
+ if (isDefault) R.string.kg_password_default_puk_message
+ else R.string.kg_password_wrong_puk_code
+ icuMessageFormat(resources, msgId, attemptsRemaining)
+ } else {
+ val msgId =
+ if (isDefault) R.string.kg_puk_enter_puk_hint
+ else R.string.kg_password_puk_failed
+ resources.getString(msgId)
+ }
+ if (isEsimLocked) {
+ displayMessage =
+ resources.getString(R.string.kg_sim_lock_esim_instructions, displayMessage)
+ }
+ return displayMessage
+ }
+
+ companion object {
+ private const val TAG = "BouncerSimInteractor"
+ const val INVALID_SUBSCRIPTION_ID = SimBouncerRepositoryImpl.INVALID_SUBSCRIPTION_ID
+ const val MIN_SIM_PIN_LENGTH = 4
+ const val MAX_SIM_PIN_LENGTH = 8
+ const val MIN_SIM_PUK_LENGTH = 8
+ const val CRITICAL_NUM_OF_ATTEMPTS = 2
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
new file mode 100644
index 000000000000..5385442092b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.bouncer.ui.helper
+
+import androidx.annotation.VisibleForTesting
+
+/** Enumerates all known adaptive layout configurations. */
+enum class BouncerSceneLayout {
+ /** The default UI with the bouncer laid out normally. */
+ STANDARD,
+ /** The bouncer is displayed vertically stacked with the user switcher. */
+ STACKED,
+ /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
+ SIDE_BY_SIDE,
+ /** The bouncer is split in two with both sides shown side-by-side. */
+ SPLIT,
+}
+
+/** Enumerates the supported window size classes. */
+enum class SizeClass {
+ COMPACT,
+ MEDIUM,
+ EXPANDED,
+}
+
+/**
+ * Internal version of `calculateLayout` in the System UI Compose library, extracted here to allow
+ * for testing that's not dependent on Compose.
+ */
+@VisibleForTesting
+fun calculateLayoutInternal(
+ width: SizeClass,
+ height: SizeClass,
+ isSideBySideSupported: Boolean,
+): BouncerSceneLayout {
+ return when (height) {
+ SizeClass.COMPACT -> BouncerSceneLayout.SPLIT
+ SizeClass.MEDIUM ->
+ when (width) {
+ SizeClass.COMPACT -> BouncerSceneLayout.STANDARD
+ SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD
+ SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE
+ }
+ SizeClass.EXPANDED ->
+ when (width) {
+ SizeClass.COMPACT -> BouncerSceneLayout.STANDARD
+ SizeClass.MEDIUM -> BouncerSceneLayout.STACKED
+ SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE
+ }
+ }.takeIf { it != BouncerSceneLayout.SIDE_BY_SIDE || isSideBySideSupported }
+ ?: BouncerSceneLayout.STANDARD
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 55bc653fc737..80248744c25a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -18,7 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import android.annotation.StringRes
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -62,6 +62,13 @@ sealed class AuthMethodBouncerViewModel(
/** Notifies that the UI has been shown to the user. */
fun onShown() {
+ interactor.resetMessage()
+ }
+
+ /**
+ * Notifies that the UI has been hidden from the user (after any transitions have completed).
+ */
+ fun onHidden() {
clearInput()
interactor.resetMessage()
}
@@ -75,7 +82,7 @@ sealed class AuthMethodBouncerViewModel(
* Notifies that the input method editor (for example, the software keyboard) has been shown or
* hidden.
*/
- fun onImeVisibilityChanged(isVisible: Boolean) {
+ suspend fun onImeVisibilityChanged(isVisible: Boolean) {
if (isImeVisible && !isVisible) {
interactor.onImeHidden()
}
@@ -113,8 +120,6 @@ sealed class AuthMethodBouncerViewModel(
}
_animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
- // TODO(b/291528545): On success, this should only be cleared after the view is animated
- // away).
clearInput()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 73d15f0f369b..44ddd9740186 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,9 +20,10 @@ import android.content.Context
import android.graphics.Bitmap
import androidx.core.graphics.drawable.toBitmap
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -64,6 +65,7 @@ class BouncerViewModel(
users: Flow<List<UserViewModel>>,
userSwitcherMenu: Flow<List<UserActionViewModel>>,
actionButtonInteractor: BouncerActionButtonInteractor,
+ private val simBouncerInteractor: SimBouncerInteractor,
) {
val selectedUserImage: StateFlow<Bitmap?> =
selectedUser
@@ -180,6 +182,19 @@ class BouncerViewModel(
initialValue = isSideBySideSupported(authMethodViewModel.value),
)
+ /**
+ * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
+ * is required.
+ */
+ val isFoldSplitRequired: StateFlow<Boolean> =
+ authMethodViewModel
+ .map { authMethod -> isFoldSplitRequired(authMethod) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = isFoldSplitRequired(authMethodViewModel.value),
+ )
+
init {
if (flags.isEnabled()) {
applicationScope.launch {
@@ -212,6 +227,10 @@ class BouncerViewModel(
return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
}
+ private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
+ return authMethod !is PasswordBouncerViewModel
+ }
+
private fun toMessageViewModel(
message: String?,
isThrottled: Boolean,
@@ -242,6 +261,17 @@ class BouncerViewModel(
viewModelScope = newViewModelScope,
interactor = bouncerInteractor,
isInputEnabled = isInputEnabled,
+ simBouncerInteractor = simBouncerInteractor,
+ authenticationMethod = authenticationMethod
+ )
+ is AuthenticationMethodModel.Sim ->
+ PinBouncerViewModel(
+ applicationContext = applicationContext,
+ viewModelScope = newViewModelScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ simBouncerInteractor = simBouncerInteractor,
+ authenticationMethod = authenticationMethod,
)
is AuthenticationMethodModel.Password ->
PasswordBouncerViewModel(
@@ -299,6 +329,7 @@ object BouncerViewModelModule {
flags: SceneContainerFlags,
userSwitcherViewModel: UserSwitcherViewModel,
actionButtonInteractor: BouncerActionButtonInteractor,
+ simBouncerInteractor: SimBouncerInteractor,
): BouncerViewModel {
return BouncerViewModel(
applicationContext = applicationContext,
@@ -311,6 +342,7 @@ object BouncerViewModelModule {
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
actionButtonInteractor = actionButtonInteractor,
+ simBouncerInteractor = simBouncerInteractor,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index fe7741930d33..a15698e1f90c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.bouncer.ui.viewmodel
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index d301085e823d..b1c5ab6122fe 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -18,7 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
import android.util.TypedValue
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
@@ -94,24 +94,23 @@ class PatternBouncerViewModel(
* @param yPx The vertical coordinate of the position of the user's pointer, in pixels.
* @param containerSizePx The size of the container of the dot grid, in pixels. It's assumed
* that the dot grid is perfectly square such that width and height are equal.
- * @param verticalOffsetPx How far down from `0` does the dot grid start on the display.
*/
- fun onDrag(xPx: Float, yPx: Float, containerSizePx: Int, verticalOffsetPx: Float) {
+ fun onDrag(xPx: Float, yPx: Float, containerSizePx: Int) {
val cellWidthPx = containerSizePx / columnCount
val cellHeightPx = containerSizePx / rowCount
- if (xPx < 0 || yPx < verticalOffsetPx) {
+ if (xPx < 0 || yPx < 0) {
return
}
val dotColumn = (xPx / cellWidthPx).toInt()
- val dotRow = ((yPx - verticalOffsetPx) / cellHeightPx).toInt()
+ val dotRow = (yPx / cellHeightPx).toInt()
if (dotColumn > columnCount - 1 || dotRow > rowCount - 1) {
return
}
val dotPixelX = dotColumn * cellWidthPx + cellWidthPx / 2
- val dotPixelY = dotRow * cellHeightPx + cellHeightPx / 2 + verticalOffsetPx
+ val dotPixelY = dotRow * cellHeightPx + cellHeightPx / 2
val distance = sqrt((xPx - dotPixelX).pow(2) + (yPx - dotPixelY).pow(2))
val hitRadius = hitFactor * min(cellWidthPx, cellHeightPx) / 2
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index b90e25526726..e25e82fe04c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -14,20 +14,26 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
import com.android.keyguard.PinShapeAdapter
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
@@ -35,13 +41,23 @@ class PinBouncerViewModel(
viewModelScope: CoroutineScope,
interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
+ private val simBouncerInteractor: SimBouncerInteractor,
+ authenticationMethod: AuthenticationMethodModel,
) :
AuthMethodBouncerViewModel(
viewModelScope = viewModelScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
) {
-
+ /**
+ * Whether the sim-related UI in the pin view is showing.
+ *
+ * This UI is used to unlock a locked sim.
+ */
+ val isSimAreaVisible = authenticationMethod == AuthenticationMethodModel.Sim
+ val isLockedEsim: StateFlow<Boolean?> = simBouncerInteractor.isLockedEsim
+ val errorDialogMessage: StateFlow<String?> = simBouncerInteractor.errorDialogMessage
+ val isSimUnlockingDialogVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
val pinShapes = PinShapeAdapter(applicationContext)
private val mutablePinInput = MutableStateFlow(PinInputViewModel.empty())
@@ -49,7 +65,13 @@ class PinBouncerViewModel(
val pinInput: StateFlow<PinInputViewModel> = mutablePinInput
/** The length of the PIN for which we should show a hint. */
- val hintedPinLength: StateFlow<Int?> = interactor.hintedPinLength
+ val hintedPinLength: StateFlow<Int?> =
+ if (isSimAreaVisible) {
+ flowOf(null)
+ } else {
+ interactor.hintedPinLength
+ }
+ .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
/** Appearance of the backspace button. */
val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
@@ -80,10 +102,32 @@ class PinBouncerViewModel(
initialValue = ActionButtonAppearance.Hidden,
)
- override val authenticationMethod = AuthenticationMethodModel.Pin
+ override val authenticationMethod: AuthenticationMethodModel = authenticationMethod
override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
+ init {
+ viewModelScope.launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
+ }
+
+ /** Notifies that the user dismissed the sim pin error dialog. */
+ fun onErrorDialogDismissed() {
+ viewModelScope.launch { simBouncerInteractor.onErrorDialogDismissed() }
+ }
+
+ /**
+ * Whether the digit buttons should be animated when touched. Note that this doesn't affect the
+ * delete or enter buttons; those should always animate.
+ */
+ val isDigitButtonAnimationEnabled: StateFlow<Boolean> =
+ interactor.isPinEnhancedPrivacyEnabled
+ .map { !it }
+ .stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !interactor.isPinEnhancedPrivacyEnabled.value,
+ )
+
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
val pinInput = mutablePinInput.value
@@ -110,7 +154,28 @@ class PinBouncerViewModel(
/** Notifies that the user clicked the "enter" button. */
fun onAuthenticateButtonClicked() {
- tryAuthenticate(useAutoConfirm = false)
+ if (authenticationMethod == AuthenticationMethodModel.Sim) {
+ viewModelScope.launch {
+ isSimUnlockingDialogVisible.value = true
+ val msg = simBouncerInteractor.verifySim(getInput())
+ interactor.setMessage(msg)
+ isSimUnlockingDialogVisible.value = false
+ clearInput()
+ }
+ } else {
+ tryAuthenticate(useAutoConfirm = false)
+ }
+ }
+
+ fun onDisableEsimButtonClicked() {
+ viewModelScope.launch { simBouncerInteractor.disableEsim() }
+ }
+
+ /** Resets the sim screen and shows a default message. */
+ private fun onResetSimFlow() {
+ simBouncerInteractor.resetSimPukUserInput()
+ interactor.resetMessage()
+ clearInput()
}
override fun clearInput() {
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
new file mode 100644
index 000000000000..27c9b3fa7c9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.common.data
+
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class CommonDataLayerModule {
+ @Binds abstract fun bindRepository(impl: ConfigurationRepositoryImpl): ConfigurationRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt
new file mode 100644
index 000000000000..7be2eaf7b105
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/CommonDomainLayerModule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.common.domain
+
+import com.android.systemui.common.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.domain.interactor.ConfigurationInteractorImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class CommonDomainLayerModule {
+ @Binds abstract fun bindInteractor(impl: ConfigurationInteractorImpl): ConfigurationInteractor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt
new file mode 100644
index 000000000000..89053d1d05fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/ConfigurationInteractor.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.common.domain.interactor
+
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.view.Surface
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+interface ConfigurationInteractor {
+ /**
+ * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
+ * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
+ * foldable devices)
+ */
+ val naturalMaxBounds: Flow<Rect>
+}
+
+class ConfigurationInteractorImpl
+@Inject
+constructor(private val repository: ConfigurationRepository) : ConfigurationInteractor {
+
+ override val naturalMaxBounds: Flow<Rect>
+ get() = repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
+
+ /**
+ * Returns screen size adjusted to rotation, so returned screen size is stable across all
+ * rotations
+ */
+ private val Configuration.naturalScreenBounds: Rect
+ get() {
+ val rotation = windowConfiguration.displayRotation
+ val maxBounds = windowConfiguration.maxBounds
+ return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+ Rect(0, 0, maxBounds.width(), maxBounds.height())
+ } else {
+ Rect(0, 0, maxBounds.height(), maxBounds.width())
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 6c45af2a8729..3cdb57318e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -36,3 +36,7 @@ sealed class Icon {
override val contentDescription: ContentDescription?,
) : Icon()
}
+
+/** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
+fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon =
+ Icon.Loaded(this, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
index 48d374207388..48d3fe000ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
@@ -23,4 +23,6 @@ data class SharedNotificationContainerPosition(
/** Whether any modifications to top/bottom are smoothly animated */
val animate: Boolean = false,
-)
+) {
+ val height: Float = bottom - top
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt
deleted file mode 100644
index b0e69317e0ee..000000000000
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt
+++ /dev/null
@@ -1,21 +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.common.ui.data
-
-import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryModule
-import dagger.Module
-
-@Module(includes = [ConfigurationRepositoryModule::class]) object CommonUiDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index 7fa762a6614f..2052c70e740d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -22,7 +22,7 @@ import android.content.res.Configuration
import android.view.DisplayInfo
import androidx.annotation.DimenRes
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+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.statusbar.policy.ConfigurationController
@@ -49,6 +49,7 @@ interface ConfigurationRepository {
val onConfigurationChange: Flow<Unit>
val scaleForResolution: Flow<Float>
+ val configurationValues: Flow<Configuration>
fun getResolutionScale(): Float
@@ -68,7 +69,7 @@ constructor(
private val displayInfo = MutableStateFlow(DisplayInfo())
override val onAnyConfigurationChange: Flow<Unit> =
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
val callback =
object : ConfigurationController.ConfigurationListener {
override fun onUiModeChanged() {
@@ -92,7 +93,7 @@ constructor(
}
override val onConfigurationChange: Flow<Unit> =
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
val callback =
object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration) {
@@ -103,6 +104,20 @@ constructor(
awaitClose { configurationController.removeCallback(callback) }
}
+ override val configurationValues: Flow<Configuration> =
+ conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration) {
+ trySend(newConfig)
+ }
+ }
+
+ trySend(context.resources.configuration)
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
+
override val scaleForResolution: StateFlow<Float> =
onConfigurationChange
.mapLatest { getResolutionScale() }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
new file mode 100644
index 000000000000..8d04e3d338a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.common.ui.view
+
+import android.view.View
+
+/**
+ * Set this view's [View#importantForAccessibility] to [View#IMPORTANT_FOR_ACCESSIBILITY_YES] or
+ * [View#IMPORTANT_FOR_ACCESSIBILITY_NO] based on [value].
+ */
+fun View.setImportantForAccessibilityYesNo(value: Boolean) {
+ importantForAccessibility =
+ if (value) View.IMPORTANT_FOR_ACCESSIBILITY_YES else View.IMPORTANT_FOR_ACCESSIBILITY_NO
+}
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 273adcf83271..847b98e82d80 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,12 +16,17 @@
package com.android.systemui.communal.dagger
+import android.content.Context
import com.android.systemui.communal.data.db.CommunalDatabaseModule
import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
+import com.android.systemui.dagger.qualifiers.Application
import dagger.Module
+import dagger.Provides
@Module(
includes =
@@ -33,4 +38,11 @@ import dagger.Module
CommunalDatabaseModule::class,
]
)
-class CommunalModule
+class CommunalModule {
+ @Provides
+ fun provideEditWidgetsActivityStarter(
+ @Application context: Context
+ ): EditWidgetsActivityStarter {
+ return EditWidgetsActivityStarterImpl(context)
+ }
+}
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 e50850d9cbbc..a12db6f8f346 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
@@ -91,7 +91,8 @@ constructor(
interface CommunalWidgetDao {
@Query(
"SELECT * FROM communal_widget_table JOIN communal_item_rank_table " +
- "ON communal_item_rank_table.uid = communal_widget_table.item_id"
+ "ON communal_item_rank_table.uid = communal_widget_table.item_id " +
+ "ORDER BY communal_item_rank_table.rank DESC"
)
fun getWidgets(): Flow<Map<CommunalItemRank, CommunalWidgetItem>>
@@ -112,6 +113,17 @@ interface CommunalWidgetDao {
@Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)")
fun insertItemRank(rank: Int): Long
+ @Query("UPDATE communal_item_rank_table SET rank = :order WHERE uid = :itemUid")
+ fun updateItemRank(itemUid: Long, order: Int)
+
+ @Transaction
+ fun updateWidgetOrder(ids: List<Int>) {
+ ids.forEachIndexed { index, it ->
+ val widget = getWidgetByIdNow(it)
+ updateItemRank(widget.itemId, ids.size - index)
+ }
+ }
+
@Transaction
fun addWidget(widgetId: Int, provider: ComponentName, priority: Int): Long {
return insertWidget(
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 6b27ce011e16..ded5581a3034 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,11 @@ package com.android.systemui.communal.data.repository
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.content.pm.PackageManager
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -32,13 +30,10 @@ 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.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -58,9 +53,6 @@ import kotlinx.coroutines.launch
/** Encapsulates the state of widgets for communal mode. */
interface CommunalWidgetRepository {
- /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
- val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
-
/** A flow of information about active communal widgets stored in database. */
val communalWidgets: Flow<List<CommunalWidgetContentModel>>
@@ -69,6 +61,9 @@ interface CommunalWidgetRepository {
/** Delete a widget by id from app widget service and the database. */
fun deleteWidget(widgetId: Int) {}
+
+ /** Update the order of widgets in the database. */
+ fun updateWidgetOrder(ids: List<Int>) {}
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -84,15 +79,12 @@ constructor(
communalRepository: CommunalRepository,
private val communalWidgetHost: CommunalWidgetHost,
private val communalWidgetDao: CommunalWidgetDao,
- private val packageManager: PackageManager,
private val userManager: UserManager,
private val userTracker: UserTracker,
@CommunalLog logBuffer: LogBuffer,
- featureFlags: FeatureFlagsClassic,
) : CommunalWidgetRepository {
companion object {
const val TAG = "CommunalWidgetRepository"
- const val WIDGET_LABEL = "Stopwatch"
}
private val logger = Logger(logBuffer, TAG)
@@ -100,9 +92,6 @@ constructor(
// Whether the [AppWidgetHost] is listening for updates.
private var isHostListening = false
- // Widgets that should be rendered in communal mode.
- private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
-
private val isUserUnlocked: Flow<Boolean> =
callbackFlow {
if (!communalRepository.isCommunalEnabled) {
@@ -149,25 +138,6 @@ constructor(
}
}
- override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
- isHostActive.map { isHostActive ->
- if (!isHostActive || !featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return@map null
- }
-
- val providerInfo =
- appWidgetManager.installedProviders.find {
- it.loadLabel(packageManager).equals(WIDGET_LABEL)
- }
-
- if (providerInfo == null) {
- logger.w("Cannot find app widget: $WIDGET_LABEL")
- return@map null
- }
-
- return@map addStopWatchWidget(providerInfo)
- }
-
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
isHostActive.flatMapLatest { isHostActive ->
if (!isHostActive) {
@@ -198,6 +168,15 @@ constructor(
}
}
+ override fun updateWidgetOrder(ids: List<Int>) {
+ applicationScope.launch(bgDispatcher) {
+ communalWidgetDao.updateWidgetOrder(ids)
+ logger.i({ "Updated the order of widget list with ids: $str1." }) {
+ str1 = ids.toString()
+ }
+ }
+ }
+
private fun mapToContentModel(
entry: Map.Entry<CommunalItemRank, CommunalWidgetItem>
): CommunalWidgetContentModel {
@@ -226,21 +205,4 @@ constructor(
appWidgetHost.stopListening()
isHostListening = false
}
-
- // TODO(b/306471933): remove this prototype that shows a stopwatch in the communal blueprint
- private fun addStopWatchWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
- val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
- if (existing != null) {
- return existing
- }
-
- val appWidgetId = appWidgetHost.allocateAppWidgetId()
- val widget =
- CommunalAppWidgetInfo(
- providerInfo,
- appWidgetId,
- )
- widgets[appWidgetId] = widget
- return widget
- }
}
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 eb36b19972f9..e630fd4e3c54 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
@@ -23,16 +23,16 @@ import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -47,17 +47,15 @@ constructor(
private val widgetRepository: CommunalWidgetRepository,
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
- tutorialInteractor: CommunalTutorialInteractor,
+ keyguardInteractor: KeyguardInteractor,
private val appWidgetHost: AppWidgetHost,
+ private val editWidgetsActivityStarter: EditWidgetsActivityStarter
) {
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
get() = communalRepository.isCommunalEnabled
- /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
- val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
-
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
@@ -71,11 +69,18 @@ constructor(
val isCommunalShowing: Flow<Boolean> =
communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }
+ val isKeyguardVisible: Flow<Boolean> = keyguardInteractor.isKeyguardVisible
+
/** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
fun onSceneChanged(newScene: CommunalSceneKey) {
communalRepository.setDesiredScene(newScene)
}
+ /** Show the widget editor Activity. */
+ fun showWidgetEditor() {
+ editWidgetsActivityStarter.startActivity()
+ }
+
/** Add a widget at the specified position. */
fun addWidget(componentName: ComponentName, priority: Int) =
widgetRepository.addWidget(componentName, priority)
@@ -83,20 +88,11 @@ constructor(
/** Delete a widget by id. */
fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
- /** A list of all the communal content to be displayed in the communal hub. */
- @OptIn(ExperimentalCoroutinesApi::class)
- val communalContent: Flow<List<CommunalContentModel>> =
- tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
- if (isTutorialMode) {
- return@flatMapLatest flowOf(tutorialContent)
- }
- combine(smartspaceContent, umoContent, widgetContent) { smartspace, umo, widgets ->
- smartspace + umo + widgets
- }
- }
+ /** Reorder widgets. The order in the list will be their display order in the hub. */
+ fun updateWidgetOrder(ids: List<Int>) = widgetRepository.updateWidgetOrder(ids)
/** A list of widget content to be displayed in the communal hub. */
- private val widgetContent: Flow<List<CommunalContentModel.Widget>> =
+ val widgetContent: Flow<List<CommunalContentModel.Widget>> =
widgetRepository.communalWidgets.map { widgets ->
widgets.map Widget@{ widget ->
return@Widget CommunalContentModel.Widget(
@@ -108,7 +104,7 @@ constructor(
}
/** A flow of available smartspace content. Currently only showing timer targets. */
- private val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
+ val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
flowOf(emptyList())
} else {
@@ -130,7 +126,7 @@ constructor(
}
/** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
- private val tutorialContent: List<CommunalContentModel.Tutorial> =
+ val tutorialContent: List<CommunalContentModel.Tutorial> =
listOf(
CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
@@ -142,9 +138,10 @@ constructor(
CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
)
- private val umoContent: Flow<List<CommunalContentModel.Umo>> =
+ val umoContent: Flow<List<CommunalContentModel.Umo>> =
mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
if (mediaPlaying) {
+ // TODO(b/310254801): support HALF and FULL layouts
flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
} else {
flowOf(emptyList())
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
deleted file mode 100644
index 0daf7b5610e8..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
+++ /dev/null
@@ -1,80 +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.communal.ui.adapter
-
-import android.appwidget.AppWidgetHost
-import android.appwidget.AppWidgetManager
-import android.content.Context
-import android.util.SizeF
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
-import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.Logger
-import com.android.systemui.log.dagger.CommunalLog
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-
-/** Transforms a [CommunalAppWidgetInfo] to a view that renders the widget. */
-class CommunalWidgetViewAdapter
-@Inject
-constructor(
- @Application private val context: Context,
- private val appWidgetManager: AppWidgetManager,
- private val appWidgetHost: AppWidgetHost,
- @CommunalLog logBuffer: LogBuffer,
-) {
- companion object {
- private const val TAG = "CommunalWidgetViewAdapter"
- }
-
- private val logger = Logger(logBuffer, TAG)
-
- fun adapt(providerInfoFlow: Flow<CommunalAppWidgetInfo?>): Flow<CommunalWidgetWrapper?> =
- providerInfoFlow.map {
- if (it == null) {
- return@map null
- }
-
- val appWidgetId = it.appWidgetId
- val providerInfo = it.providerInfo
-
- if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, providerInfo.provider)) {
- logger.d("Success binding app widget id: $appWidgetId")
- return@map CommunalWidgetWrapper(context).apply {
- addView(
- appWidgetHost.createView(context, appWidgetId, providerInfo).apply {
- // Set the widget to minimum width and height
- updateAppWidgetSize(
- appWidgetManager.getAppWidgetOptions(appWidgetId),
- listOf(
- SizeF(
- providerInfo.minResizeWidth.toFloat(),
- providerInfo.minResizeHeight.toFloat()
- )
- )
- )
- }
- )
- }
- } else {
- logger.w("Failed binding app widget id")
- return@map null
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
deleted file mode 100644
index 65bf4b3e69cd..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
+++ /dev/null
@@ -1,70 +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.communal.ui.binder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.launch
-
-/** Binds [CommunalWidgetViewModel] to the keyguard root view. */
-object CommunalWidgetViewBinder {
-
- @JvmStatic
- fun bind(
- rootView: KeyguardRootView,
- viewModel: CommunalWidgetViewModel,
- adapter: CommunalWidgetViewAdapter,
- keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
- ) {
- rootView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- adapter.adapt(viewModel.appWidgetInfo).collect {
- val oldView =
- rootView.findViewById<CommunalWidgetWrapper>(
- R.id.communal_widget_wrapper
- )
- var dirty = false
-
- if (oldView != null) {
- rootView.removeView(oldView)
- dirty = true
- }
-
- if (it != null) {
- rootView.addView(it)
- dirty = true
- }
-
- if (dirty) {
- keyguardBlueprintInteractor.refreshBlueprint()
- }
- }
- }
-
- launch { viewModel.alpha.collect { rootView.alpha = it } }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index 702554ab4943..9198c7bd7009 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -30,13 +29,11 @@ class DefaultCommunalBlueprint
@Inject
constructor(
defaultCommunalHubSection: DefaultCommunalHubSection,
- defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
override val sections: List<KeyguardSection> =
listOf(
defaultCommunalHubSection,
- defaultCommunalWidgetSection,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
deleted file mode 100644
index c3e8a96701ea..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ /dev/null
@@ -1,73 +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.communal.ui.view.layout.sections
-
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import dagger.Lazy
-import javax.inject.Inject
-
-class DefaultCommunalWidgetSection
-@Inject
-constructor(
- private val featureFlags: FeatureFlags,
- private val keyguardRootView: KeyguardRootView,
- private val communalWidgetViewModel: CommunalWidgetViewModel,
- private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
- private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
-) : KeyguardSection() {
- private val widgetAreaViewId = R.id.communal_widget_wrapper
-
- override fun addViews(constraintLayout: ConstraintLayout) {}
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return
- }
-
- CommunalWidgetViewBinder.bind(
- keyguardRootView,
- communalWidgetViewModel,
- communalWidgetViewAdapter,
- keyguardBlueprintInteractor.get(),
- )
- }
-
- override fun applyConstraints(constraintSet: ConstraintSet) {
- constraintSet.apply {
- constrainWidth(widgetAreaViewId, WRAP_CONTENT)
- constrainHeight(widgetAreaViewId, WRAP_CONTENT)
- connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM)
- connect(widgetAreaViewId, END, PARENT_ID, END)
- }
- }
-
- override fun removeViews(constraintLayout: ConstraintLayout) {}
-}
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
new file mode 100644
index 000000000000..4d8e893cb747
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.communal.ui.viewmodel
+
+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.media.controls.ui.MediaHost
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+
+/** The base view model for the communal hub. */
+abstract class BaseCommunalViewModel(
+ private val communalInteractor: CommunalInteractor,
+ val mediaHost: MediaHost,
+) {
+ val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible
+
+ val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
+
+ fun onSceneChanged(scene: CommunalSceneKey) {
+ communalInteractor.onSceneChanged(scene)
+ }
+
+ /** A list of all the communal content to be displayed in the communal hub. */
+ abstract val communalContent: Flow<List<CommunalContentModel>>
+
+ /** Whether in edit mode for the communal hub. */
+ open val isEditMode = false
+
+ /** Called as the UI requests deleting a widget. */
+ open fun onDeleteWidget(id: Int) {}
+
+ /** Called as the UI requests reordering widgets. */
+ open fun onReorderWidgets(ids: List<Int>) {}
+
+ /** Called as the UI requests opening the widget editor. */
+ open fun onOpenWidgetEditor() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index d7bbea64332e..111f8b4ca48f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -17,22 +17,30 @@
package com.android.systemui.communal.ui.viewmodel
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.flow.Flow
+/** The view model for communal hub in edit mode. */
@SysUISingleton
-class CommunalWidgetViewModel
+class CommunalEditModeViewModel
@Inject
constructor(
- communalInteractor: CommunalInteractor,
- keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
-) {
- /** An observable for the alpha level for the communal widget area. */
- val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+ private val communalInteractor: CommunalInteractor,
+ @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
+) : BaseCommunalViewModel(communalInteractor, mediaHost) {
- /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
- val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo
+ override val isEditMode = true
+
+ // Only widgets are editable.
+ override val communalContent: Flow<List<CommunalContentModel>> =
+ communalInteractor.widgetContent
+
+ override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
+
+ override fun onReorderWidgets(ids: List<Int>) = communalInteractor.updateWidgetOrder(ids)
}
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 5efe6ceeb65a..11bde6bd7af0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,49 +16,43 @@
package com.android.systemui.communal.ui.viewmodel
-import android.content.ComponentName
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
import javax.inject.Named
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+/** The default view model used for showing the communal hub. */
@SysUISingleton
class CommunalViewModel
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
- @Named(MediaModule.COMMUNAL_HUB) val mediaHost: MediaHost,
-) {
- val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
- fun onSceneChanged(scene: CommunalSceneKey) {
- communalInteractor.onSceneChanged(scene)
- }
+ tutorialInteractor: CommunalTutorialInteractor,
+ @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
+) : BaseCommunalViewModel(communalInteractor, mediaHost) {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override val communalContent: Flow<List<CommunalContentModel>> =
+ tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
+ if (isTutorialMode) {
+ return@flatMapLatest flowOf(communalInteractor.tutorialContent)
+ }
+ combine(
+ communalInteractor.smartspaceContent,
+ communalInteractor.umoContent,
+ communalInteractor.widgetContent,
+ ) { smartspace, umo, widgets ->
+ smartspace + umo + widgets
+ }
+ }
- /** A list of all the communal content to be displayed in the communal hub. */
- val communalContent: Flow<List<CommunalContentModel>> = communalInteractor.communalContent
-
- /** Delete a widget by id. */
- fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
-
- /** Open the widget picker */
- fun onOpenWidgetPicker() {
- // STOPSHIP(b/306500486): refactor this when integrating with the widget picker.
- // Eventually clicking on this button will bring up the widget picker and inside
- // the widget picker, addWidget will be called to add the user selected widget.
- // For now, a stopwatch widget will be added to the end of the grid.
- communalInteractor.addWidget(
- componentName =
- ComponentName(
- "com.google.android.deskclock",
- "com.android.alarmclock.StopwatchAppWidgetProvider"
- ),
- priority = 0
- )
- }
+ override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
new file mode 100644
index 000000000000..7b94fc182fe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.communal.widgets
+
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
+import javax.inject.Inject
+
+/** An Activity for editing the widgets that appear in hub mode. */
+class EditWidgetsActivity
+@Inject
+constructor(
+ private val communalViewModel: CommunalEditModeViewModel,
+ private val communalInteractor: CommunalInteractor,
+) : ComponentActivity() {
+ companion object {
+ /**
+ * Intent extra name for the {@link AppWidgetProviderInfo} of a widget to add to hub mode.
+ */
+ const val ADD_WIDGET_INFO = "add_widget_info"
+ private const val TAG = "EditWidgetsActivity"
+ }
+
+ private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
+ registerForActivityResult(StartActivityForResult()) { result ->
+ when (result.resultCode) {
+ RESULT_OK -> {
+ result.data
+ ?.let {
+ it.getParcelableExtra(
+ ADD_WIDGET_INFO,
+ AppWidgetProviderInfo::class.java
+ )
+ }
+ ?.let { communalInteractor.addWidget(it.provider, 0) }
+ ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+ }
+ else ->
+ Log.w(
+ TAG,
+ "Failed to receive result from widget picker, code=${result.resultCode}"
+ )
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setCommunalEditWidgetActivityContent(
+ activity = this,
+ viewModel = communalViewModel,
+ onOpenWidgetPicker = {
+ addWidgetActivityLauncher.launch(
+ Intent(applicationContext, WidgetPickerActivity::class.java)
+ )
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
new file mode 100644
index 000000000000..846e3000284f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.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.communal.widgets
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.dagger.qualifiers.Application
+
+interface EditWidgetsActivityStarter {
+ fun startActivity()
+}
+
+class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) :
+ EditWidgetsActivityStarter {
+ override fun startActivity() {
+ applicationContext.startActivity(
+ Intent(applicationContext, EditWidgetsActivity::class.java)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
new file mode 100644
index 000000000000..a2765486bf2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.communal.widgets
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * An Activity responsible for displaying a list of widgets to add to the hub mode grid. This is
+ * essentially a placeholder until Launcher's widget picker can be used.
+ */
+class WidgetPickerActivity
+@Inject
+constructor(
+ private val appWidgetManager: AppWidgetManager,
+) : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.widget_picker)
+
+ loadWidgets()
+ }
+
+ private fun loadWidgets() {
+ val containerView: ViewGroup? = findViewById(R.id.widgets_container)
+ containerView?.apply {
+ try {
+ appWidgetManager
+ .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+ ?.stream()
+ ?.limit(5)
+ ?.forEach { widgetInfo ->
+ val activity = this@WidgetPickerActivity
+ val widgetPreview =
+ widgetInfo.loadPreviewImage(activity, DisplayMetrics.DENSITY_HIGH)
+ val widgetView = ImageView(activity)
+ val lp = LinearLayout.LayoutParams(WIDGET_PREVIEW_SIZE, WIDGET_PREVIEW_SIZE)
+ widgetView.setLayoutParams(lp)
+ widgetView.setImageDrawable(widgetPreview)
+ widgetView.setOnClickListener({
+ setResult(
+ RESULT_OK,
+ Intent().putExtra(EditWidgetsActivity.ADD_WIDGET_INFO, widgetInfo)
+ )
+ finish()
+ })
+
+ addView(widgetView)
+ }
+ } catch (e: RuntimeException) {
+ Log.e(TAG, "Exception fetching widget providers", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val WIDGET_PREVIEW_SIZE = 400
+ private const val TAG = "WidgetPickerActivity"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 4bdea75d9d71..65d44957222a 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -22,7 +22,7 @@ import android.view.View
import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.scene.shared.model.Scene
@@ -58,6 +58,13 @@ interface BaseComposeFacade {
onResult: (PeopleViewModel.Result) -> Unit,
)
+ /** Bind the content of [activity] to [viewModel]. */
+ fun setCommunalEditWidgetActivityContent(
+ activity: ComponentActivity,
+ viewModel: BaseCommunalViewModel,
+ onOpenWidgetPicker: () -> Unit,
+ )
+
/** Create a [View] to represent [viewModel] on screen. */
fun createFooterActionsView(
context: Context,
@@ -77,9 +84,9 @@ interface BaseComposeFacade {
/** Create a [View] to represent [viewModel] on screen. */
fun createCommunalView(
context: Context,
- viewModel: CommunalViewModel,
+ viewModel: BaseCommunalViewModel,
): View
/** Creates a container that hosts the communal UI and handles gesture transitions. */
- fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View
+ fun createCommunalContainer(context: Context, viewModel: BaseCommunalViewModel): View
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 32e40c99a981..4b27af1fc989 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,8 @@ package com.android.systemui.dagger;
import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.communal.widgets.EditWidgetsActivity;
+import com.android.systemui.communal.widgets.WidgetPickerActivity;
import com.android.systemui.contrast.ContrastDialogActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
@@ -150,7 +152,17 @@ public abstract class DefaultActivityBinder {
@ClassKey(SensorUseStartedActivity.class)
public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
+ /** Inject into EditWidgetsActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(EditWidgetsActivity.class)
+ public abstract Activity bindEditWidgetsActivity(EditWidgetsActivity activity);
+ /** Inject into WidgetPickerActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(WidgetPickerActivity.class)
+ public abstract Activity bindWidgetPickerActivity(WidgetPickerActivity activity);
/** Inject into SwitchToManagedProfileForCallActivity. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index d8ff535ffd78..d041acb4601a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -22,7 +22,7 @@ import com.android.systemui.LatencyTester
import com.android.systemui.ScreenDecorations
import com.android.systemui.SliceBroadcastRelayHandler
import com.android.systemui.accessibility.SystemActions
-import com.android.systemui.accessibility.WindowMagnification
+import com.android.systemui.accessibility.Magnification
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.BiometricNotificationService
@@ -37,7 +37,6 @@ import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
-import com.android.systemui.keyguard.ui.binder.AlternateBouncerBinder
import com.android.systemui.keyguard.ui.binder.KeyguardDismissActionBinder
import com.android.systemui.keyguard.ui.binder.KeyguardDismissBinder
import com.android.systemui.log.SessionTracker
@@ -92,11 +91,6 @@ abstract class SystemUICoreStartableModule {
@ClassKey(AuthController::class)
abstract fun bindAuthController(service: AuthController): CoreStartable
- @Binds
- @IntoMap
- @ClassKey(AlternateBouncerBinder::class)
- abstract fun bindAlternateBouncerBinder(impl: AlternateBouncerBinder): CoreStartable
-
/** Inject into BiometricNotificationService */
@Binds
@IntoMap
@@ -254,11 +248,11 @@ abstract class SystemUICoreStartableModule {
@ClassKey(VolumeUI::class)
abstract fun bindVolumeUI(sysui: VolumeUI): CoreStartable
- /** Inject into WindowMagnification. */
+ /** Inject into Magnification. */
@Binds
@IntoMap
- @ClassKey(WindowMagnification::class)
- abstract fun bindWindowMagnification(sysui: WindowMagnification): CoreStartable
+ @ClassKey(Magnification::class)
+ abstract fun bindMagnification(sysui: Magnification): CoreStartable
/** Inject into WMShell. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index b34b4599cf70..0405ca43320f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -37,11 +37,13 @@ import com.android.systemui.biometrics.FingerprintReEnrollNotification;
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule;
+import com.android.systemui.bouncer.data.repository.BouncerRepositoryModule;
import com.android.systemui.bouncer.domain.interactor.BouncerInteractorModule;
import com.android.systemui.bouncer.ui.BouncerViewModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
-import com.android.systemui.common.ui.data.CommonUiDataLayerModule;
+import com.android.systemui.common.data.CommonDataLayerModule;
+import com.android.systemui.common.domain.CommonDomainLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -59,6 +61,7 @@ import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -104,8 +107,6 @@ import com.android.systemui.statusbar.notification.collection.inflation.Notifica
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
@@ -120,6 +121,7 @@ import com.android.systemui.statusbar.policy.PolicyModule;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.statusbar.ui.binder.StatusBarViewBinderModule;
import com.android.systemui.statusbar.window.StatusBarWindowModule;
import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
@@ -127,6 +129,7 @@ import com.android.systemui.tuner.dagger.TunerModule;
import com.android.systemui.unfold.SysUIUnfoldModule;
import com.android.systemui.user.UserModule;
import com.android.systemui.user.domain.UserDomainLayerModule;
+import com.android.systemui.util.EventLogModule;
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
import com.android.systemui.util.kotlin.CoroutinesModule;
@@ -169,11 +172,13 @@ import javax.inject.Named;
BiometricsModule.class,
BiometricsDomainLayerModule.class,
BouncerInteractorModule.class,
+ BouncerRepositoryModule.class,
BouncerViewModule.class,
ClipboardOverlayModule.class,
ClockRegistryModule.class,
- CommonUiDataLayerModule.class,
CommunalModule.class,
+ CommonDataLayerModule.class,
+ CommonDomainLayerModule.class,
ConnectivityModule.class,
ControlsModule.class,
CoroutinesModule.class,
@@ -182,6 +187,7 @@ import javax.inject.Named;
DisableFlagsModule.class,
DisplayModule.class,
DreamModule.class,
+ EventLogModule.class,
FalsingModule.class,
FlagsModule.class,
FlagDependenciesModule.class,
@@ -189,6 +195,7 @@ import javax.inject.Named;
KeyEventRepositoryModule.class,
KeyboardModule.class,
KeyguardBlueprintModule.class,
+ KeyguardSectionsModule.class,
LetterboxModule.class,
LogModule.class,
MediaProjectionModule.class,
@@ -215,6 +222,7 @@ import javax.inject.Named;
StatusBarModule.class,
StatusBarPipelineModule.class,
StatusBarPolicyModule.class,
+ StatusBarViewBinderModule.class,
StatusBarWindowModule.class,
SystemPropertiesFlagsModule.class,
SysUIConcurrencyModule.class,
@@ -366,11 +374,4 @@ public abstract class SystemUIModule {
@Binds
abstract LargeScreenShadeInterpolator largeScreensShadeInterpolator(
LargeScreenShadeInterpolatorImpl impl);
-
- @SysUISingleton
- @Provides
- static VisualInterruptionDecisionProvider provideVisualInterruptionDecisionProvider(
- NotificationInterruptStateProvider innerProvider) {
- return new NotificationInterruptStateProviderWrapper(innerProvider);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 1e29e1fa3197..f27bbe6c7624 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.deviceentry.data.repository
+import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -15,9 +16,12 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -35,10 +39,14 @@ interface DeviceEntryRepository {
val isUnlocked: StateFlow<Boolean>
/**
- * Whether the lockscreen should be shown when the authentication method is not secure (e.g.
- * `None` or `Swipe`).
+ * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has
+ * chosen any secure authentication method and even if they set the lockscreen to be dismissed
+ * when the user swipes on it.
*/
- suspend fun isInsecureLockscreenEnabled(): Boolean
+ suspend fun isLockscreenEnabled(): Boolean
+
+ /** Report successful authentication for device entry. */
+ fun reportSuccessfulAuthentication()
/**
* Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
@@ -67,7 +75,9 @@ constructor(
keyguardStateController: KeyguardStateController,
) : DeviceEntryRepository {
- override val isUnlocked =
+ private val _isUnlocked = MutableStateFlow(false)
+
+ private val isUnlockedReportedByLegacyKeyguard =
conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -99,19 +109,27 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) }
}
.distinctUntilChanged()
+ .onEach { _isUnlocked.value = it }
.stateIn(
applicationScope,
SharingStarted.Eagerly,
initialValue = false,
)
- override suspend fun isInsecureLockscreenEnabled(): Boolean {
+ override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
+
+ override suspend fun isLockscreenEnabled(): Boolean {
return withContext(backgroundDispatcher) {
val selectedUserId = userRepository.getSelectedUserInfo().id
!lockPatternUtils.isLockScreenDisabled(selectedUserId)
}
}
+ override fun reportSuccessfulAuthentication() {
+ Log.d(TAG, "Successful authentication reported.")
+ _isUnlocked.value = true
+ }
+
override val isBypassEnabled: StateFlow<Boolean> =
conflatedCallbackFlow {
val listener =
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index e872d13bd913..715fb17c7c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -17,23 +17,29 @@
package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
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.scene.shared.model.SceneModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Hosts application business logic related to device entry.
@@ -41,6 +47,7 @@ import kotlinx.coroutines.flow.stateIn
* Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless
* of the authentication method used.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class DeviceEntryInteractor
@Inject
@@ -48,9 +55,10 @@ constructor(
@Application private val applicationScope: CoroutineScope,
repository: DeviceEntryRepository,
private val authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
+ private val sceneInteractor: SceneInteractor,
deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository,
trustRepository: TrustRepository,
+ flags: SceneContainerFlags,
) {
/**
* Whether the device is unlocked.
@@ -66,7 +74,8 @@ constructor(
repository.isUnlocked,
authenticationInteractor.authenticationMethod,
) { isUnlocked, authenticationMethod ->
- !authenticationMethod.isSecure || isUnlocked
+ (!authenticationMethod.isSecure || isUnlocked) &&
+ authenticationMethod != AuthenticationMethodModel.Sim
}
.stateIn(
scope = applicationScope,
@@ -90,28 +99,33 @@ constructor(
.map { it == SceneKey.Gone }
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = false,
)
// Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
private val passivelyAuthenticated =
merge(
- trustRepository.isCurrentUserTrusted,
- deviceEntryFaceAuthRepository.isAuthenticated,
- )
+ trustRepository.isCurrentUserTrusted,
+ deviceEntryFaceAuthRepository.isAuthenticated,
+ )
+ .onStart { emit(false) }
/**
* Whether it's currently possible to swipe up to enter the device without requiring
- * authentication. This returns `false` whenever the lockscreen has been dismissed.
+ * authentication or when the device is already authenticated using a passive authentication
+ * mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
+ * dismissed.
*
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
val canSwipeToEnter =
combine(
+ // This is true when the user has chosen to show the lockscreen but has not made it
+ // secure.
authenticationInteractor.authenticationMethod.map {
- it == AuthenticationMethodModel.Swipe
+ it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
passivelyAuthenticated,
isDeviceEntered
@@ -120,11 +134,37 @@ constructor(
}
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = false,
)
/**
+ * Attempt to enter the device and dismiss the lockscreen. If authentication is required to
+ * unlock the device it will transition to bouncer.
+ */
+ fun attemptDeviceEntry() {
+ // TODO (b/307768356),
+ // 1. Check if the device is already authenticated by trust agent/passive biometrics
+ // 2. show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
+ // 3. For face auth only setups trigger face auth, delay transitioning to bouncer for
+ // a small amount of time.
+ // 4. Transition to bouncer scene
+ applicationScope.launch {
+ if (isAuthenticationRequired()) {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Bouncer),
+ loggingReason = "request to unlock device while authentication required",
+ )
+ } else {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Gone),
+ loggingReason = "request to unlock device while authentication isn't required",
+ )
+ }
+ }
+ }
+
+ /**
* Returns `true` if the device currently requires authentication before entry is granted;
* `false` if the device can be entered without authenticating first.
*/
@@ -133,10 +173,22 @@ constructor(
}
/**
- * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
+ * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
* dismissed once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
- * lock screen.
+ * lockscreen.
*/
val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
+
+ init {
+ if (flags.isEnabled()) {
+ applicationScope.launch {
+ authenticationInteractor.authenticationChallengeResult.collectLatest { successful ->
+ if (successful) {
+ repository.reportSuccessfulAuthentication()
+ }
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
new file mode 100644
index 000000000000..72b9da669360
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for device entry under-display fingerprint state changes. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryUdfpsInteractor
+@Inject
+constructor(
+ // TODO (b/309655554): create & use interactors for these repositories
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
+ fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ biometricSettingsRepository: BiometricSettingsRepository,
+) {
+ /** Whether the device supports an under display fingerprint sensor. */
+ val isUdfpsSupported: Flow<Boolean> =
+ fingerprintPropertyRepository.sensorType.map { it.isUdfps() }
+
+ /** Whether the under-display fingerprint sensor is enrolled and enabled for device entry. */
+ val isUdfpsEnrolledAndEnabled: Flow<Boolean> =
+ combine(isUdfpsSupported, biometricSettingsRepository.isFingerprintEnrolledAndEnabled) {
+ udfps,
+ fpEnrolledAndEnabled ->
+ udfps && fpEnrolledAndEnabled
+ }
+ /** Whether the under display fingerprint sensor is currently running. */
+ val isListeningForUdfps =
+ isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ fingerprintAuthRepository.isRunning
+ } else {
+ flowOf(false)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
new file mode 100644
index 000000000000..b5d5803ca6fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.deviceentry.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the device entry udfps refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object DeviceEntryUdfpsRefactor {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.deviceEntryUdfpsRefactor()
+
+ /**
+ * 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/dreams/complication/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
index 410a0c53a492..ee48ee5f50fd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
@@ -71,6 +71,7 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
private final Runnable mRestoreComplications = new Runnable() {
@Override
public void run() {
+ Log.d(TAG, "Restoring complications...");
mVisibilityController.setVisibility(View.VISIBLE);
mHidden = false;
}
@@ -83,6 +84,7 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
// Avoid interfering with the exit animations.
return;
}
+ Log.d(TAG, "Hiding complications...");
mVisibilityController.setVisibility(View.INVISIBLE);
mHidden = true;
if (mHiddenCallback != null) {
@@ -136,9 +138,7 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
final MotionEvent motionEvent = (MotionEvent) ev;
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
- if (DEBUG) {
- Log.d(TAG, "ACTION_DOWN received");
- }
+ Log.i(TAG, "ACTION_DOWN received");
final ListenableFuture<Boolean> touchCheck = mTouchInsetManager
.checkWithinTouchRegion(Math.round(motionEvent.getX()),
@@ -163,6 +163,8 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
}, mExecutor);
} else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL
|| motionEvent.getAction() == MotionEvent.ACTION_UP) {
+ Log.i(TAG, "ACTION_CANCEL|ACTION_UP received");
+
// End session and initiate delayed reappearance of the complications.
session.pop();
runAfterHidden(() -> mCancelCallbacks.add(
@@ -179,8 +181,10 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
private void runAfterHidden(Runnable runnable) {
mExecutor.execute(() -> {
if (mHidden) {
+ Log.i(TAG, "Executing after hidden runnable immediately...");
runnable.run();
} else {
+ Log.i(TAG, "Queuing after hidden runnable...");
mHiddenCallback = runnable;
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index c924df6da263..349236551ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -45,7 +45,7 @@ open class DumpManager @Inject constructor() {
/** See [registerCriticalDumpable]. */
fun registerCriticalDumpable(module: Dumpable) {
- registerCriticalDumpable(module::class.java.simpleName, module)
+ registerCriticalDumpable(module::class.java.canonicalName, module)
}
/**
@@ -62,7 +62,7 @@ open class DumpManager @Inject constructor() {
/** See [registerNormalDumpable]. */
fun registerNormalDumpable(module: Dumpable) {
- registerNormalDumpable(module::class.java.simpleName, module)
+ registerNormalDumpable(module::class.java.canonicalName, module)
}
/**
@@ -105,12 +105,12 @@ open class DumpManager @Inject constructor() {
}
/**
- * Same as the above override, but automatically uses the simple class name as the dumpable
+ * Same as the above override, but automatically uses the canonical class name as the dumpable
* name.
*/
@Synchronized
fun registerDumpable(module: Dumpable) {
- registerDumpable(module::class.java.simpleName, module)
+ registerDumpable(module::class.java.canonicalName, module)
}
/** Unregisters a previously-registered dumpable. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
index dd5860484a55..906896fb920e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
@@ -17,9 +17,9 @@
package com.android.systemui.flags
import android.util.Log
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.ConditionalRestarter.Condition
-import com.android.systemui.util.kotlin.UnflaggedApplication
-import com.android.systemui.util.kotlin.UnflaggedBackground
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Named
@@ -39,8 +39,8 @@ constructor(
private val systemExitRestarter: SystemExitRestarter,
private val conditions: Set<@JvmSuppressWildcards Condition>,
@Named(RESTART_DELAY) private val restartDelaySec: Long,
- @UnflaggedApplication private val applicationScope: CoroutineScope,
- @UnflaggedBackground private val backgroundDispatcher: CoroutineContext,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineContext,
) : Restarter {
private var pendingReason = ""
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index da3fd14cdcf0..83c16ae9ea78 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -17,7 +17,6 @@
package com.android.systemui.flags
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.Flags as Classic
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import javax.inject.Inject
@@ -28,9 +27,5 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha
FlagDependenciesBase(featureFlags, handler) {
override fun defineDependencies() {
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
-
- // These two flags are effectively linked. We should migrate them to a single aconfig flag.
- Classic.MIGRATE_NSSL dependsOn Classic.MIGRATE_KEYGUARD_STATUS_VIEW
- Classic.MIGRATE_KEYGUARD_STATUS_VIEW dependsOn Classic.MIGRATE_NSSL
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index dd971b966e7f..98fda3eb01c5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -135,14 +135,6 @@ object Flags {
"lockscreen_custom_clocks"
)
- // TODO(b/286092087): Tracking Bug
- @JvmField
- val ENABLE_SYSTEM_UI_DREAM_CONTROLLER = unreleasedFlag("enable_system_ui_dream_controller")
-
- // TODO(b/288287730): Tracking Bug
- @JvmField
- val ENABLE_SYSTEM_UI_DREAM_HOSTING = unreleasedFlag("enable_system_ui_dream_hosting")
-
/**
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
* the digits when the clock moves.
@@ -156,18 +148,6 @@ object Flags {
// TODO(b/255607168): Tracking Bug
@JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
- /**
- * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
- * new KeyguardTransitionRepository.
- */
- // TODO(b/281655028): Tracking bug
- @JvmField
- val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = true)
-
- // TODO(b/301915812): Tracking Bug
- @JvmField
- val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true)
-
// TODO(b/305984787):
@JvmField
val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
@@ -232,15 +212,9 @@ object Flags {
val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Whether to run the new udfps keyguard refactor code. */
- // TODO(b/279440316): Tracking bug.
- @JvmField
- val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag("refactor_udfps_keyguard_views")
-
/** Provide new auth messages on the bouncer. */
// TODO(b/277961132): Tracking bug.
- @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages",
- teamfood = true)
+ @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
/** Keyguard Migration */
@@ -255,17 +229,6 @@ object Flags {
// TODO(b/287268101): Tracking bug.
@JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
- // TODO(b/288276738): Tracking bug.
- @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
-
- /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */
- // TODO(b/288074305): Tracking bug.
- @JvmField val MIGRATE_NSSL = unreleasedFlag("migrate_nssl")
-
- /** Migrate the status view from the notification panel to keyguard root view. */
- // TODO(b/291767565): Tracking bug.
- @JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag("migrate_keyguard_status_view")
-
/** Migrate the status bar view on keyguard from notification panel to keyguard root view. */
// TODO(b/299115332): Tracking Bug.
@JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
@@ -305,11 +268,6 @@ object Flags {
R.bool.flag_stop_pulsing_face_scanning_animation,
"stop_pulsing_face_scanning_animation")
- /** Flag to use a separate view for the alternate bouncer. */
- // TODO(b/300440924): Tracking bug
- @JvmField
- val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
-
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -320,11 +278,6 @@ object Flags {
val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
releasedFlag("smartspace_shared_element_transition_enabled")
- // TODO(b/258517050): Clean up after the feature is launched.
- @JvmField
- val SMARTSPACE_DATE_WEATHER_DECOUPLED =
- sysPropBooleanFlag("persist.sysui.ss.dw_decoupled", default = true)
-
// TODO(b/270223352): Tracking Bug
@JvmField
val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = releasedFlag("hide_smartspace_on_dream_overlay")
@@ -345,13 +298,6 @@ object Flags {
"qs_user_detail_shortcut"
)
- @JvmField
- val QS_PIPELINE_NEW_HOST = unreleasedFlag("qs_pipeline_new_host", teamfood = true)
-
- // TODO(b/278068252): Tracking Bug
- @JvmField
- val QS_PIPELINE_AUTO_ADD = unreleasedFlag("qs_pipeline_auto_add", teamfood = true)
-
// TODO(b/296357483): Tracking Bug
@JvmField
val QS_PIPELINE_NEW_TILES = unreleasedFlag("qs_pipeline_new_tiles")
@@ -380,6 +326,10 @@ object Flags {
// TODO(b/301610137): Tracking bug
@JvmField val NEW_NETWORK_SLICE_UI = releasedFlag("new_network_slice_ui")
+ // TODO(b/311222557): Tracking bug
+ val ROAMING_INDICATOR_VIA_DISPLAY_INFO =
+ releasedFlag("roaming_indicator_via_display_info")
+
// TODO(b/308138154): Tracking bug
val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
releasedFlag("filter_provisioning_network_subscriptions")
@@ -452,9 +402,6 @@ object Flags {
// TODO(b/270437894): Tracking Bug
val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
- // TODO(b/304506662): Tracking Bug
- val MEDIA_DEVICE_NAME_FIX = releasedFlag("media_device_name_fix")
-
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
@@ -517,12 +464,6 @@ object Flags {
val WALLPAPER_MULTI_CROP =
sysPropBooleanFlag("persist.wm.debug.wallpaper_multi_crop", default = false)
- // TODO(b/290220798): Tracking Bug
- @Keep
- @JvmField
- val ENABLE_PIP2_IMPLEMENTATION =
- sysPropBooleanFlag("persist.wm.debug.enable_pip2_implementation", default = false)
-
// 1200 - predictive back
@Keep
@JvmField
@@ -649,10 +590,6 @@ object Flags {
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- @JvmField
- val COROUTINE_TRACING =
- unreleasedFlag("coroutine_tracing")
-
// TODO(b/283071711): Tracking bug
@JvmField
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
@@ -748,12 +685,6 @@ object Flags {
@JvmField
val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
- // 3100 - Haptic interactions
-
- // TODO(b/290213663): Tracking Bug
- @JvmField
- val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration")
-
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
@@ -783,9 +714,4 @@ object Flags {
@JvmField
val COMMUNAL_SERVICE_ENABLED = resourceBooleanFlag(R.bool.config_communalServiceEnabled,
"communal_service_enabled")
-
- // TODO(b/303131306): Tracking Bug
- /** Whether communal hub features are enabled. */
- @JvmField
- val COMMUNAL_HUB = unreleasedFlag("communal_hub")
}
diff --git a/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt b/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt
new file mode 100644
index 000000000000..bc1cc4f4bc56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.fold.ui.helper
+
+import androidx.annotation.VisibleForTesting
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+
+sealed interface FoldPosture {
+ /** A foldable device that's fully closed/folded or a device that doesn't support folding. */
+ data object Folded : FoldPosture
+ /** A foldable that's halfway open with the hinge held vertically. */
+ data object Book : FoldPosture
+ /** A foldable that's halfway open with the hinge held horizontally. */
+ data object Tabletop : FoldPosture
+ /** A foldable that's fully unfolded / flat. */
+ data object FullyUnfolded : FoldPosture
+}
+
+/**
+ * Internal version of `foldPosture` in the System UI Compose library, extracted here to allow for
+ * testing that's not dependent on Compose.
+ */
+@VisibleForTesting
+fun foldPostureInternal(layoutInfo: WindowLayoutInfo?): FoldPosture {
+ return layoutInfo
+ ?.displayFeatures
+ ?.firstNotNullOfOrNull { it as? FoldingFeature }
+ .let { foldingFeature ->
+ when (foldingFeature?.state) {
+ null -> FoldPosture.Folded
+ FoldingFeature.State.HALF_OPENED -> foldingFeature.orientation.toHalfwayPosture()
+ FoldingFeature.State.FLAT ->
+ if (foldingFeature.isSeparating) {
+ // Dual screen device.
+ foldingFeature.orientation.toHalfwayPosture()
+ } else {
+ FoldPosture.FullyUnfolded
+ }
+ else -> error("Unsupported state \"${foldingFeature.state}\"")
+ }
+ }
+}
+
+private fun FoldingFeature.Orientation.toHalfwayPosture(): FoldPosture {
+ return when (this) {
+ FoldingFeature.Orientation.HORIZONTAL -> FoldPosture.Tabletop
+ FoldingFeature.Orientation.VERTICAL -> FoldPosture.Book
+ else -> error("Unsupported orientation \"$this\"")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index e16bb0bb8482..1e9be09bc3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -30,6 +30,7 @@ import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.view.Window
import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -78,23 +79,29 @@ class KeyboardBacklightDialog(
private lateinit var stepProperties: StepViewProperties
@ColorInt
- var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ private val filledRectangleColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
@ColorInt
- var emptyRectangleColor =
+ private val emptyRectangleColor =
getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
@ColorInt
- var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
+ private val backgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
@ColorInt
- var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ private val defaultIconColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
@ColorInt
- var defaultIconBackgroundColor =
+ private val defaultIconBackgroundColor =
getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
@ColorInt
- var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ private val dimmedIconColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
@ColorInt
- var dimmedIconBackgroundColor =
+ private val dimmedIconBackgroundColor =
getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
+ private val levelContentDescription = context.getString(R.string.keyboard_backlight_value)
+
init {
currentLevel = initialCurrentLevel
maxLevel = initialMaxLevel
@@ -103,6 +110,8 @@ class KeyboardBacklightDialog(
override fun onCreate(savedInstanceState: Bundle?) {
setUpWindowProperties(this)
setWindowPosition()
+ // title is used for a11y announcement
+ window?.setTitle(context.getString(R.string.keyboard_backlight_dialog_title))
updateResources()
rootView = buildRootView()
setContentView(rootView)
@@ -159,6 +168,12 @@ class KeyboardBacklightDialog(
currentLevel = current
updateIconTile()
updateStepColors()
+ updateAccessibilityInfo()
+ }
+
+ private fun updateAccessibilityInfo() {
+ rootView.contentDescription = String.format(levelContentDescription, currentLevel, maxLevel)
+ rootView.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
}
private fun updateIconTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 1037b0eb4dfc..017dac200431 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -31,8 +31,8 @@ import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
@@ -134,7 +134,7 @@ constructor(
val indicationArea = KeyguardIndicationArea(context, null)
keyguardIndicationController.setIndicationArea(indicationArea)
- if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
lockIconViewController.get().setLockIconView(LockIconView(context, null))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4e6a872cb3f7..fe9865b2d1dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2720,9 +2720,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
mUiBgExecutor.execute(() -> {
- if (DEBUG) {
- Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
- }
+ Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
// Handled in WmLockscreenVisibilityManager if flag is enabled.
@@ -3251,10 +3249,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
DejankUtils.postAfterTraversal(() -> {
if (!mPM.isInteractive() && !mPendingLock) {
Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal:"
- + "mPM.isInteractive()=" + mPM.isInteractive()
- + "mPendingLock=" + mPendingLock + "."
- + "One of these being false means we re-locked the device during unlock. "
- + "Do not proceed to finish keyguard exit and unlock.");
+ + " mPM.isInteractive()=" + mPM.isInteractive()
+ + " mPendingLock=" + mPendingLock + "."
+ + " One of these being false means we re-locked the device during unlock."
+ + " Do not proceed to finish keyguard exit and unlock.");
doKeyguardLocked(null);
finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
// Ensure WM is notified that we made a decision to show
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 8b93b171d241..331d892304b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -55,6 +55,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -94,6 +95,7 @@ import kotlinx.coroutines.CoroutineDispatcher;
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
+ DeviceEntryIconTransitionModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 8d5d73f88ca1..5c76be80f1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -49,8 +49,10 @@ import kotlinx.coroutines.flow.filter
* [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on
* this repository.
*
- * To print all transitions to logcat to help with debugging, run this command: adb shell settings
- * put global systemui/buffer/KeyguardLog VERBOSE
+ * To print all transitions to logcat to help with debugging, run this command:
+ * ```
+ * adb shell cmd statusbar echo -b KeyguardLog:VERBOSE
+ * ```
*
* This will print all keyguard transitions to logcat with the KeyguardTransitionAuditLogger tag.
*/
@@ -175,9 +177,11 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
override fun onAnimationStart(animation: Animator) {
emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
+
override fun onAnimationCancel(animation: Animator) {
endAnimation(lastStep.value, TransitionState.CANCELED)
}
+
override fun onAnimationEnd(animation: Animator) {
endAnimation(1f, TransitionState.FINISHED)
}
@@ -222,7 +226,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
}
private fun emitTransition(nextStep: TransitionStep, isManual: Boolean = false) {
- trace(nextStep, isManual)
+ logAndTrace(nextStep, isManual)
val emitted = _transitions.tryEmit(nextStep)
if (!emitted) {
Log.w(TAG, "Failed to emit next value without suspending")
@@ -230,26 +234,22 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
lastStep = nextStep
}
- private fun trace(step: TransitionStep, isManual: Boolean) {
+ private fun logAndTrace(step: TransitionStep, isManual: Boolean) {
if (step.transitionState == TransitionState.RUNNING) {
return
}
- val traceName =
- "Transition: ${step.from} -> ${step.to} " +
- if (isManual) {
- "(manual)"
- } else {
- ""
- }
+ val manualStr = if (isManual) " (manual)" else ""
+ val traceName = "Transition: ${step.from} -> ${step.to}$manualStr"
+
val traceCookie = traceName.hashCode()
- if (step.transitionState == TransitionState.STARTED) {
- Trace.beginAsyncSection(traceName, traceCookie)
- } else if (
- step.transitionState == TransitionState.FINISHED ||
- step.transitionState == TransitionState.CANCELED
- ) {
- Trace.endAsyncSection(traceName, traceCookie)
+ when (step.transitionState) {
+ TransitionState.STARTED -> Trace.beginAsyncSection(traceName, traceCookie)
+ TransitionState.FINISHED -> Trace.endAsyncSection(traceName, traceCookie)
+ TransitionState.CANCELED -> Trace.endAsyncSection(traceName, traceCookie)
+ else -> {}
}
+
+ Log.i(TAG, "${step.transitionState.name} transition: $step$manualStr")
}
companion object {
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 54031dcc9525..cb0f18630324 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
@@ -22,6 +22,7 @@ import android.content.Context
import android.graphics.Point
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -33,6 +34,8 @@ import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -42,8 +45,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
-import kotlin.math.max
val DEFAULT_REVEAL_EFFECT = LiftReveal
@@ -72,8 +73,13 @@ constructor(
keyguardRepository: KeyguardRepository,
val context: Context,
powerInteractor: PowerInteractor,
+ private val scrimLogger: ScrimLogger,
) : LightRevealScrimRepository {
+ companion object {
+ val TAG = LightRevealScrimRepository::class.simpleName!!
+ }
+
/** The reveal effect used if the device was locked/unlocked via the power button. */
private val powerButtonRevealEffect: Flow<LightRevealEffect?> =
flowOf(
@@ -120,25 +126,25 @@ constructor(
/** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
- powerInteractor
- .detailedWakefulness
- .flatMapLatest { wakefulnessModel ->
- when {
- wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) ->
- powerButtonRevealEffect
- wakefulnessModel.isAwakeFrom(TAP) ->
- tapRevealEffect
- else ->
- flowOf(LiftReveal)
- }
- }
+ powerInteractor.detailedWakefulness.flatMapLatest { wakefulnessModel ->
+ when {
+ wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) ->
+ powerButtonRevealEffect
+ wakefulnessModel.isAwakeFrom(TAP) -> tapRevealEffect
+ else -> flowOf(LiftReveal)
+ }
+ }
private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 500 }
override val revealAmount: Flow<Float> = callbackFlow {
val updateListener =
Animator.AnimatorUpdateListener {
- trySend((it as ValueAnimator).animatedValue as Float)
+ val value = (it as ValueAnimator).animatedValue
+ trySend(value as Float)
+ if (value <= 0.0f || value >= 1.0f) {
+ scrimLogger.d(TAG, "revealAmount", value)
+ }
}
revealAmountAnimator.addUpdateListener(updateListener)
awaitClose { revealAmountAnimator.removeUpdateListener(updateListener) }
@@ -146,6 +152,7 @@ constructor(
override fun startRevealAmountAnimator(reveal: Boolean) {
if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse()
+ scrimLogger.d(TAG, "startRevealAmountAnimator, reveal", reveal)
}
override val revealEffect =
@@ -156,13 +163,21 @@ constructor(
) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
// Use the biometric reveal for any flavor of wake and unlocking.
- when (biometricUnlockState) {
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
- else -> nonBiometricReveal
- }
- ?: DEFAULT_REVEAL_EFFECT
+ val revealEffect =
+ when (biometricUnlockState) {
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+ else -> nonBiometricReveal
+ }
+ ?: DEFAULT_REVEAL_EFFECT
+
+ scrimLogger.d(
+ TAG,
+ "revealEffect",
+ "$revealEffect, biometricUnlockState: ${biometricUnlockState.name}"
+ )
+ return@combine revealEffect
}
.distinctUntilChanged()
@@ -173,8 +188,7 @@ constructor(
x,
y,
startRadius = 0,
- endRadius =
- max(max(x, display.width - x), max(y, display.height - y)),
+ endRadius = max(max(x, display.width - x), max(y, display.height - y)),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 8bf2bc395f88..cc1cf911f1c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -50,14 +50,18 @@ constructor(
private val configurationRepository: ConfigurationRepository,
private val keyguardInteractor: KeyguardInteractor,
) {
- val udfpsBurnInXOffset: StateFlow<Int> =
+ val deviceEntryIconXOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
- val udfpsBurnInYOffset: StateFlow<Int> =
+ val deviceEntryIconYOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
- val udfpsBurnInProgress: StateFlow<Float> =
+ val udfpsProgress: StateFlow<Float> =
keyguardInteractor.dozeTimeTick
.mapLatest { burnInHelperWrapper.burnInProgressOffset() }
- .stateIn(scope, SharingStarted.Lazily, burnInHelperWrapper.burnInProgressOffset())
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ burnInHelperWrapper.burnInProgressOffset()
+ )
val keyguardBurnIn: Flow<BurnInModel> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index a331a668e135..858440185568 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -113,7 +113,9 @@ constructor(
}
companion object {
- val TO_LOCKSCREEN_DURATION = 500.milliseconds
private val DEFAULT_DURATION = 500.milliseconds
+ val TO_LOCKSCREEN_DURATION = 500.milliseconds
+ val TO_GONE_DURATION = DEFAULT_DURATION
+ val TO_OCCLUDED_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index e9719e7d584e..eca7088c079a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -26,11 +26,11 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromDozingTransitionInteractor
@@ -97,5 +97,6 @@ constructor(
companion object {
private val DEFAULT_DURATION = 500.milliseconds
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
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 ca882e539a1a..0b6b971f2314 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
@@ -54,7 +54,10 @@ constructor(
fun startToLockscreenTransition() {
scope.launch {
- if (transitionInteractor.startedKeyguardState.value == KeyguardState.DREAMING) {
+ if (
+ transitionInteractor.startedKeyguardState.replayCache.last() ==
+ KeyguardState.DREAMING
+ ) {
startTransitionTo(KeyguardState.LOCKSCREEN)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index eace0c70cb5b..bd73d60cda29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -138,6 +138,6 @@ constructor(
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
- val TO_AOD_DURATION = 1100.milliseconds
+ val TO_AOD_DURATION = 1300.milliseconds
}
}
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 ea40ba0376d4..152d2172ee4c 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
@@ -391,5 +391,7 @@ constructor(
val TO_DREAMING_DURATION = 933.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
val TO_AOD_DURATION = 500.milliseconds
+ val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+ val TO_GONE_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index dec38b504ee1..6a8555cb7f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -142,9 +142,7 @@ constructor(
::toTriple
)
.collect { (isAsleep, lastStartedStep, isAodAvailable) ->
- if (
- lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep
- ) {
+ if (lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep) {
startTransitionTo(
if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
)
@@ -187,5 +185,6 @@ constructor(
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 933.milliseconds
+ val TO_AOD_DURATION = DEFAULT_DURATION
}
}
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 24b666185ce2..5f246e181c26 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
@@ -255,5 +255,7 @@ constructor(
private val DEFAULT_DURATION = 300.milliseconds
val TO_GONE_DURATION = 500.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
+ val TO_AOD_DURATION = DEFAULT_DURATION
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
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 e256b49be8f8..949c940bdebc 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
@@ -56,6 +56,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -83,10 +84,18 @@ constructor(
shadeRepository: ShadeRepository,
sceneInteractorProvider: Provider<SceneInteractor>,
) {
- /** Position information for the shared notification container. */
- val sharedNotificationContainerPosition =
+ // TODO(b/296118689): move to a repository
+ private val _sharedNotificationContainerPosition =
MutableStateFlow(SharedNotificationContainerPosition())
+ /** Position information for the shared notification container. */
+ val sharedNotificationContainerPosition: StateFlow<SharedNotificationContainerPosition> =
+ _sharedNotificationContainerPosition.asStateFlow()
+
+ fun setSharedNotificationContainerPosition(position: SharedNotificationContainerPosition) {
+ _sharedNotificationContainerPosition.value = position
+ }
+
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index efbe2611e960..922baa3611cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -24,6 +24,7 @@ import javax.inject.Inject
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
@@ -40,18 +41,20 @@ constructor(
@OptIn(ExperimentalCoroutinesApi::class)
val viewParams: Flow<KeyguardSurfaceBehindModel> =
- transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
- if (!isInTransition) {
- defaultParams
- } else {
- combine(
- transitionSpecificViewParams,
- defaultParams,
- ) { transitionParams, defaultParams ->
- transitionParams ?: defaultParams
+ transitionInteractor.isInTransitionToAnyState
+ .flatMapLatest { isInTransition ->
+ if (!isInTransition) {
+ defaultParams
+ } else {
+ combine(
+ transitionSpecificViewParams,
+ defaultParams,
+ ) { transitionParams, defaultParams ->
+ transitionParams ?: defaultParams
+ }
}
}
- }
+ .distinctUntilChanged()
val isAnimatingSurface = repository.isAnimatingSurface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 419524fe7730..a03fa38ec850 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -75,24 +75,6 @@ constructor(
}
scope.launch {
- interactor.finishedKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Finished transition", it)
- }
- }
-
- scope.launch {
- interactor.canceledKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Canceled transition", it)
- }
- }
-
- scope.launch {
- interactor.startedKeyguardTransitionStep.collect {
- logger.log(TAG, VERBOSE, "Started transition", it)
- }
- }
-
- scope.launch {
keyguardInteractor.dozeTransitionModel.collect {
logger.log(TAG, VERBOSE, "Doze transition", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index b0b857729c77..4da48f697b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -37,14 +37,14 @@ import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.shareIn
/** Encapsulates business-logic related to the keyguard transitions. */
@SysUISingleton
@@ -171,16 +171,16 @@ constructor(
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
/** The destination state of the last started transition. */
- val startedKeyguardState: StateFlow<KeyguardState> =
+ val startedKeyguardState: SharedFlow<KeyguardState> =
startedKeyguardTransitionStep
.map { step -> step.to }
- .stateIn(scope, SharingStarted.Eagerly, OFF)
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
/** The last completed [KeyguardState] transition */
- val finishedKeyguardState: StateFlow<KeyguardState> =
+ val finishedKeyguardState: SharedFlow<KeyguardState> =
finishedKeyguardTransitionStep
.map { step -> step.to }
- .stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
/**
* Whether we're currently in a transition to a new [KeyguardState] and haven't yet completed
@@ -227,14 +227,13 @@ constructor(
* state.
*/
fun startDismissKeyguardTransition() {
- when (startedKeyguardState.value) {
+ when (val startedState = startedKeyguardState.replayCache.last()) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
else ->
Log.e(
"KeyguardTransitionInteractor",
- "We don't know how to dismiss keyguard from state " +
- "${startedKeyguardState.value}"
+ "We don't know how to dismiss keyguard from state $startedState."
)
}
}
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 6115d90430b3..2d43897c2565 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
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
@@ -37,6 +38,7 @@ constructor(
private val transitionInteractor: KeyguardTransitionInteractor,
private val lightRevealScrimRepository: LightRevealScrimRepository,
@Application private val scope: CoroutineScope,
+ private val scrimLogger: ScrimLogger,
) {
init {
@@ -46,6 +48,7 @@ constructor(
private fun listenForStartedKeyguardTransitionStep() {
scope.launch {
transitionInteractor.startedKeyguardTransitionStep.collect {
+ scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
if (willTransitionChangeEndState(it)) {
lightRevealScrimRepository.startRevealAmountAnimator(
willBeRevealedInState(it.to)
@@ -100,5 +103,7 @@ constructor(
KeyguardState.OCCLUDED -> true
}
}
+
+ val TAG = LightRevealScrimInteractor::class.simpleName!!
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt
new file mode 100644
index 000000000000..508fb597ef65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.domain.interactor
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.SettingNotFoundException
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+@SysUISingleton
+class NaturalScrollingSettingObserver
+@Inject
+constructor(
+ @Main private val handler: Handler,
+ private val context: Context,
+) {
+ var isNaturalScrollingEnabled = true
+ get() {
+ if (!isInitialized) {
+ isInitialized = true
+ update()
+ }
+ return field
+ }
+
+ private var isInitialized = false
+
+ private val contentObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean) {
+ update()
+ }
+ }
+
+ init {
+ context.contentResolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING), false,
+ contentObserver)
+ }
+
+ private fun update() {
+ isNaturalScrollingEnabled = try {
+ Settings.System.getIntForUser(context.contentResolver,
+ Settings.System.TOUCHPAD_NATURAL_SCROLLING, UserHandle.USER_CURRENT) == 1
+ } catch (e: SettingNotFoundException) {
+ true
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 76018080848f..d5ac2838a2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -53,16 +53,16 @@ sealed class TransitionInteractor(
modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE
): UUID? {
if (
- fromState != transitionInteractor.startedKeyguardState.value &&
- fromState != transitionInteractor.finishedKeyguardState.value
+ fromState != transitionInteractor.startedKeyguardState.replayCache.last() &&
+ fromState != transitionInteractor.finishedKeyguardState.replayCache.last()
) {
Log.e(
name,
"startTransition: We were asked to transition from " +
"$fromState to $toState, however we last finished a transition to " +
- "${transitionInteractor.finishedKeyguardState.value}, " +
+ "${transitionInteractor.finishedKeyguardState.replayCache.last()}, " +
"and last started a transition to " +
- "${transitionInteractor.startedKeyguardState.value}. " +
+ "${transitionInteractor.startedKeyguardState.replayCache.last()}. " +
"Ignoring startTransition, but this should never happen."
)
return null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index c0308e6c5759..f5cd7676e4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -51,14 +51,14 @@ constructor(
val scaleForResolution = configRepo.scaleForResolution
/** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
- val burnInOffsets: Flow<BurnInOffsets> =
+ val burnInOffsets: Flow<Offsets> =
combine(
keyguardInteractor.dozeAmount,
- burnInInteractor.udfpsBurnInXOffset,
- burnInInteractor.udfpsBurnInYOffset,
- burnInInteractor.udfpsBurnInProgress
+ burnInInteractor.deviceEntryIconXOffset,
+ burnInInteractor.deviceEntryIconYOffset,
+ burnInInteractor.udfpsProgress
) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
- BurnInOffsets(
+ Offsets(
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
@@ -86,8 +86,8 @@ constructor(
.onStart { emit(0f) }
}
-data class BurnInOffsets(
- val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
- val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
- val burnInProgress: Float, // current progress based on the aodTransitionAmount
+data class Offsets(
+ val x: Int, // current x burn in offset based on the aodTransitionAmount
+ val y: Int, // current y burn in offset based on the aodTransitionAmount
+ val progress: Float, // current progress based on the aodTransitionAmount
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt
new file mode 100644
index 000000000000..23642a741fb8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt
@@ -0,0 +1,52 @@
+/*
+ * 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/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 9d7477c13be6..d5ad7ab0d0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -105,4 +105,9 @@ class KeyguardTransitionAnimationFlow(
}
.filterNotNull()
}
+
+ /** Immediately (after 1ms) emits the given value for every step of the KeyguardTransition. */
+ fun immediatelyTransitionTo(value: Float): Flow<Float> {
+ return createFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 41af9e810fbf..a6383eb5f785 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -20,46 +20,15 @@ import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scrim.ScrimView
-import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.NotificationShadeWindowController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
-import javax.inject.Inject
-
-@ExperimentalCoroutinesApi
-@SysUISingleton
-class AlternateBouncerBinder
-@Inject
-constructor(
- private val notificationShadeWindowView: NotificationShadeWindowView,
- private val featureFlags: FeatureFlagsClassic,
- private val alternateBouncerViewModel: AlternateBouncerViewModel,
- @Application private val scope: CoroutineScope,
- private val notificationShadeWindowController: NotificationShadeWindowController,
-) : CoreStartable {
- override fun start() {
- if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
- return
- }
-
- AlternateBouncerViewBinder.bind(
- notificationShadeWindowView.requireViewById(R.id.alternate_bouncer),
- alternateBouncerViewModel,
- scope,
- notificationShadeWindowController,
- )
- }
-}
/**
* Binds the alternate bouncer view to its view-model.
@@ -79,6 +48,7 @@ object AlternateBouncerViewBinder {
scope: CoroutineScope,
notificationShadeWindowController: NotificationShadeWindowController,
) {
+ DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
scope.launch {
// forcePluginOpen is necessary to show over occluded apps.
// This cannot be tied to the view's lifecycle because setting this allows the view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e82ea7fecd05..4b4a19ecd770 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -23,7 +23,10 @@ import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
@@ -34,19 +37,25 @@ import kotlinx.coroutines.launch
object DeviceEntryIconViewBinder {
/**
- * Updates UI for the device entry icon view (lock, unlock and fingerprint icons) and its
- * background.
+ * Updates UI for:
+ * - device entry containing view (parent view for the below views)
+ * - long-press handling view (transparent, no UI)
+ * - foreground icon view (lock/unlock/fingerprint)
+ * - background view (optional)
*/
@SuppressLint("ClickableViewAccessibility")
@JvmStatic
fun bind(
view: DeviceEntryIconView,
viewModel: DeviceEntryIconViewModel,
+ fgViewModel: DeviceEntryForegroundViewModel,
+ bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
) {
- val iconView = view.iconView
- val bgView = view.bgView
+ DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val longPressHandlingView = view.longPressHandlingView
+ val fgIconView = view.iconView
+ val bgView = view.bgView
longPressHandlingView.listener =
object : LongPressHandlingView.Listener {
override fun onLongPressDetected(view: View, x: Int, y: Int) {
@@ -56,45 +65,69 @@ object DeviceEntryIconViewBinder {
viewModel.onLongPress()
}
}
+
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ // Repeat on CREATED so that the view will always observe the entire
+ // GONE => AOD transition (even though the view may not be visible until the middle
+ // of the transition.
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
- viewModel.iconViewModel.collect { iconViewModel ->
- iconView.setImageState(
- view.getIconState(iconViewModel.type, iconViewModel.useAodVariant),
- /* merge */ false
- )
- iconView.imageTintList = ColorStateList.valueOf(iconViewModel.tint)
- iconView.alpha = iconViewModel.alpha
- iconView.setPadding(
- iconViewModel.padding,
- iconViewModel.padding,
- iconViewModel.padding,
- iconViewModel.padding,
- )
+ viewModel.isLongPressEnabled.collect { isEnabled ->
+ longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
}
}
launch {
- viewModel.backgroundViewModel.collect { bgViewModel ->
- bgView.alpha = bgViewModel.alpha
- bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ viewModel.accessibilityDelegateHint.collect { hint ->
+ view.accessibilityHintType = hint
}
}
launch {
- viewModel.burnInViewModel.collect { burnInViewModel ->
- view.translationX = burnInViewModel.x.toFloat()
- view.translationY = burnInViewModel.y.toFloat()
- view.aodFpDrawable.progress = burnInViewModel.progress
+ viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
+ if (useBackgroundProtection) {
+ bgView.visibility = View.VISIBLE
+ } else {
+ bgView.visibility = View.GONE
+ }
}
}
launch {
- viewModel.isLongPressEnabled.collect { isEnabled ->
- longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
+ view.aodFpDrawable.progress = burnInOffsets.progress
}
}
+
+ launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
+ }
+ }
+
+ fgIconView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.accessibilityDelegateHint.collect { hint ->
- view.accessibilityHintType = hint
+ fgViewModel.viewModel.collect { viewModel ->
+ fgIconView.setImageState(
+ view.getIconState(viewModel.type, viewModel.useAodVariant),
+ /* merge */ false
+ )
+ fgIconView.imageTintList = ColorStateList.valueOf(viewModel.tint)
+ fgIconView.setPadding(
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ )
+ }
+ }
+ }
+ }
+
+ bgView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ bgViewModel.viewModel.collect { bgViewModel ->
+ bgView.alpha = bgViewModel.alpha
+ bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
deleted file mode 100644
index 5900a2467994..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
+++ /dev/null
@@ -1,128 +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.ui.binder
-
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewPropertyAnimator
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.launch
-
-object KeyguardAmbientIndicationAreaViewBinder {
- /**
- * Defines interface for an object that acts as the binding between the view and its view-model.
- *
- * Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after
- * it is bound.
- */
- interface Binding {
- /**
- * Returns a collection of [ViewPropertyAnimator] instances that can be used to animate the
- * indication areas.
- */
- fun getIndicationAreaAnimators(): List<ViewPropertyAnimator>
-
- /** Notifies that device configuration has changed. */
- fun onConfigurationChanged()
-
- /** Destroys this binding, releases resources, and cancels any coroutines. */
- fun destroy()
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- fun bind(
- view: ViewGroup,
- viewModel: KeyguardAmbientIndicationViewModel,
- keyguardRootViewModel: KeyguardRootViewModel,
- ): Binding {
- val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container)
- val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
-
- val disposableHandle =
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- keyguardRootViewModel.alpha.collect { alpha ->
- ambientIndicationArea?.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
- }
-
- launch {
- viewModel.indicationAreaTranslationX.collect { translationX ->
- ambientIndicationArea?.translationX = translationX
- }
- }
-
- launch {
- configurationBasedDimensions
- .map { it.defaultBurnInPreventionYOffsetPx }
- .flatMapLatest { defaultBurnInOffsetY ->
- viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
- }
- .collect { translationY ->
- ambientIndicationArea?.translationY = translationY
- }
- }
-
- }
- }
-
-
- return object : Binding {
- override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
- return listOf(ambientIndicationArea).mapNotNull { it?.animate() }
- }
-
- override fun onConfigurationChanged() {
- configurationBasedDimensions.value = loadFromResources(view)
- }
-
- override fun destroy() {
- disposableHandle.dispose()
- }
- }
- }
-
- private fun loadFromResources(view: View): ConfigurationBasedDimensions {
- return ConfigurationBasedDimensions(
- defaultBurnInPreventionYOffsetPx =
- view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
- )
- }
-
- private data class ConfigurationBasedDimensions(
- val defaultBurnInPreventionYOffsetPx: Int,
- )
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index c5a8375f5576..eee5206498e4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -17,13 +17,11 @@
package com.android.systemui.keyguard.ui.binder
import android.annotation.SuppressLint
-import android.content.Intent
import android.graphics.Rect
import android.graphics.drawable.Animatable2
import android.util.Size
import android.view.View
import android.view.ViewGroup
-import android.view.ViewPropertyAnimator
import android.widget.ImageView
import androidx.core.animation.CycleInterpolator
import androidx.core.animation.ObjectAnimator
@@ -34,7 +32,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableLinearLayout
@@ -43,9 +40,12 @@ import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils
+import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils.LAUNCH_SOURCE_KEYGUARD
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -79,7 +79,7 @@ object KeyguardBottomAreaViewBinder {
* Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after
* it is bound.
*/
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
@Deprecated("Deprecated as part of b/278057014")
interface Binding {
/** Notifies that device configuration has changed. */
@@ -133,8 +133,7 @@ object KeyguardBottomAreaViewBinder {
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
-
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
launch {
viewModel.startButton.collect { buttonModel ->
updateButton(
@@ -147,7 +146,7 @@ object KeyguardBottomAreaViewBinder {
}
}
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
launch {
viewModel.endButton.collect { buttonModel ->
updateButton(
@@ -185,7 +184,7 @@ object KeyguardBottomAreaViewBinder {
}
}
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
launch {
updateButtonAlpha(
view = startButton,
@@ -194,7 +193,7 @@ object KeyguardBottomAreaViewBinder {
)
}
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
launch {
updateButtonAlpha(
view = endButton,
@@ -220,7 +219,7 @@ object KeyguardBottomAreaViewBinder {
}
}
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
launch {
configurationBasedDimensions.collect { dimensions ->
startButton.updateLayoutParams<ViewGroup.LayoutParams> {
@@ -378,13 +377,14 @@ object KeyguardBottomAreaViewBinder {
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
if (viewModel.useLongPress) {
- val onTouchListener = KeyguardQuickAffordanceOnTouchListener(
- view,
- viewModel,
- messageDisplayer,
- vibratorHelper,
- falsingManager,
- )
+ val onTouchListener =
+ KeyguardQuickAffordanceOnTouchListener(
+ view,
+ viewModel,
+ messageDisplayer,
+ vibratorHelper,
+ falsingManager,
+ )
view.setOnTouchListener(onTouchListener)
view.setOnClickListener {
messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
@@ -403,9 +403,7 @@ object KeyguardBottomAreaViewBinder {
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
- shakeAnimator.doOnEnd {
- view.translationX = 0f
- }
+ shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
@@ -425,7 +423,7 @@ object KeyguardBottomAreaViewBinder {
}
@Deprecated("Deprecated as part of b/278057014")
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
private suspend fun updateButtonAlpha(
view: View,
viewModel: Flow<KeyguardQuickAffordanceViewModel>,
@@ -456,7 +454,7 @@ object KeyguardBottomAreaViewBinder {
}
@Deprecated("Deprecated as part of b/278057014")
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
private class OnLongClickListener(
private val falsingManager: FalsingManager?,
private val viewModel: KeyguardQuickAffordanceViewModel,
@@ -493,7 +491,7 @@ object KeyguardBottomAreaViewBinder {
}
@Deprecated("Deprecated as part of b/278057014")
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
private class OnClickListener(
private val viewModel: KeyguardQuickAffordanceViewModel,
private val falsingManager: FalsingManager,
@@ -535,13 +533,7 @@ object KeyguardBottomAreaViewBinder {
view: View,
) {
activityStarter.postStartActivityDismissingKeyguard(
- Intent(Intent.ACTION_SET_WALLPAPER).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- view.context
- .getString(R.string.config_wallpaperPickerPackage)
- .takeIf { it.isNotEmpty() }
- ?.let { packageName -> setPackage(packageName) }
- },
+ WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
/* delay= */ 0,
/* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
/* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
@@ -549,7 +541,7 @@ object KeyguardBottomAreaViewBinder {
}
@Deprecated("Deprecated as part of b/278057014")
- //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
+ // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
private data class ConfigurationBasedDimensions(
val defaultBurnInPreventionYOffsetPx: Int,
val buttonSizePx: Size,
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 1f74bb661135..114fd94ebeb2 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
@@ -34,14 +34,14 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.Icon
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.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.RefactorFlag
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
@@ -116,7 +116,7 @@ object KeyguardRootViewBinder {
launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
}
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
launch {
viewModel.burnInLayerVisibility.collect { visibility ->
childViews[burnInLayerId]?.visibility = visibility
@@ -342,7 +342,7 @@ object KeyguardRootViewBinder {
featureFlags: FeatureFlagsClassic,
screenOffAnimationController: ScreenOffAnimationController,
): DisposableHandle? {
- RefactorFlag(featureFlags, Flags.MIGRATE_KEYGUARD_STATUS_VIEW).assertInLegacyMode()
+ KeyguardShadeMigrationNssl.assertInLegacyMode()
if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return null
return view.repeatWhenAttached {
lifecycleScope.launch {
@@ -368,7 +368,7 @@ object KeyguardRootViewBinder {
iconsAppearTranslationPx: Int,
screenOffAnimationController: ScreenOffAnimationController,
) {
- val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
+ val statusViewMigrated = KeyguardShadeMigrationNssl.isEnabled
animate().cancel()
val animatorListener =
object : AnimatorListenerAdapter() {
@@ -384,7 +384,7 @@ object KeyguardRootViewBinder {
}
visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
}
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
+ newAodTransition() -> {
animateInIconTranslation(statusViewMigrated)
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 6beef8ec1ff3..8514225fda90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -17,19 +17,20 @@
package com.android.systemui.keyguard.ui.binder
-import android.content.Intent
import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils
+import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils.LAUNCH_SOURCE_KEYGUARD
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -98,13 +99,7 @@ object KeyguardSettingsViewBinder {
view: View,
) {
activityStarter.postStartActivityDismissingKeyguard(
- Intent(Intent.ACTION_SET_WALLPAPER).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- view.context
- .getString(R.string.config_wallpaperPickerPackage)
- .takeIf { it.isNotEmpty() }
- ?.let { packageName -> setPackage(packageName) }
- },
+ WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
/* delay= */ 0,
/* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
/* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
@@ -127,5 +122,4 @@ object KeyguardSettingsViewBinder {
}
.start()
}
-
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
index 9872d97021fa..52d87d369083 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -42,9 +42,9 @@ object UdfpsAodFingerprintViewBinder {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
- view.progress = burnInOffsets.burnInProgress
- view.translationX = burnInOffsets.burnInXOffset.toFloat()
- view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ view.progress = burnInOffsets.progress
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
index bab04f234b3f..d4621e6e2356 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -59,8 +59,8 @@ object UdfpsFingerprintViewBinder {
launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
- view.translationX = burnInOffsets.burnInXOffset.toFloat()
- view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index bdd9a6bf3f79..a2e930c49511 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -49,7 +49,7 @@ import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -445,7 +445,7 @@ constructor(
private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
largeClockHostView =
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large)
} else {
val hostView = FrameLayout(previewContext)
@@ -460,7 +460,7 @@ constructor(
largeClockHostView.isInvisible = true
smallClockHostView =
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view)
} else {
val resources = parentView.resources
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
new file mode 100644
index 000000000000..b58a80ff8d34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.transitions
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Each DeviceEntryIconTransition is responsible for updating the given parameters for the current
+ * keyguard transition.
+ * *
+ * MUST list implementing classes in dagger module [DeviceEntryIconTransitionModule].
+ */
+interface DeviceEntryIconTransition {
+ val deviceEntryParentViewAlpha: Flow<Float>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
new file mode 100644
index 000000000000..9d557bb8a3b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.ui.transitions
+
+import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Module
+abstract class DeviceEntryIconTransitionModule {
+ @Binds
+ @IntoSet
+ abstract fun aodToLockscreen(
+ impl: AodToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun aodToGone(impl: AodToGoneTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dozingToLockscreen(
+ impl: DozingToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingToLockscreen(
+ impl: DreamingToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToAod(
+ impl: LockscreenToAodTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToDreaming(
+ impl: LockscreenToDreamingTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToOccluded(
+ impl: LockscreenToOccludedTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToPrimaryBouncer(
+ impl: LockscreenToPrimaryBouncerTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToGone(
+ impl: LockscreenToGoneTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun goneToAod(impl: GoneToAodTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun occludedToAod(impl: OccludedToAodTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun occludedToLockscreen(
+ impl: OccludedToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToAod(
+ impl: PrimaryBouncerToAodTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToLockscreen(
+ impl: PrimaryBouncerToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index c9e395402dad..af1d0df92652 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -40,7 +40,10 @@ constructor(
attrs: AttributeSet?,
defStyleAttrs: Int = 0,
) : FrameLayout(context, attrs, defStyleAttrs) {
- val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+ val longPressHandlingView: LongPressHandlingView =
+ LongPressHandlingView(context, attrs) {
+ context.resources.getInteger(R.integer.config_lockIconLongPress).toLong()
+ }
val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
val aodFpDrawable: LottieDrawable = LottieDrawable()
@@ -105,7 +108,7 @@ constructor(
// FINGERPRINT
animatedIconDrawable.addState(
getIconState(IconType.FINGERPRINT, false),
- context.getDrawable(R.drawable.ic_kg_fingerprint)!!,
+ context.getDrawable(R.drawable.ic_fingerprint)!!,
R.id.locked_fp,
)
@@ -220,7 +223,7 @@ constructor(
val lp = longPressHandlingView.layoutParams as LayoutParams
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
- longPressHandlingView.setLayoutParams(lp)
+ longPressHandlingView.layoutParams = lp
}
private fun addIconImageView() {
@@ -231,7 +234,7 @@ constructor(
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
lp.gravity = Gravity.CENTER
- iconView.setLayoutParams(lp)
+ iconView.layoutParams = lp
}
private fun addBgImageView() {
@@ -240,7 +243,7 @@ constructor(
val lp = bgView.layoutParams as LayoutParams
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
- bgView.setLayoutParams(lp)
+ bgView.layoutParams = lp
}
fun getIconState(icon: IconType, aod: Boolean): IntArray {
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 2e64c41bace8..27b38c71d2e7 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
@@ -20,10 +20,10 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.items.ClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
@@ -31,8 +31,12 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopu
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
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.KeyguardSectionsModule.Companion.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
+import kotlin.jvm.optionals.getOrNull
/**
* Positions elements of the lockscreen to the default position.
@@ -47,7 +51,8 @@ constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultShortcutsSection: DefaultShortcutsSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
@@ -56,16 +61,15 @@ constructor(
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
clockSection: ClockSection,
- smartspaceSection: SmartspaceSection
+ smartspaceSection: SmartspaceSection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
- defaultDeviceEntryIconSection,
defaultShortcutsSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
defaultStatusBarSection,
@@ -74,7 +78,8 @@ constructor(
aodBurnInSection,
communalTutorialIndicatorSection,
clockSection,
- smartspaceSection
+ smartspaceSection,
+ defaultDeviceEntryIconSection, // Add LAST: Intentionally has z-order above other views.
)
companion object {
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 d8b368b4a0d3..190ad44845d0 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
@@ -19,18 +19,22 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
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.KeyguardSectionsModule
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
/** Vertically aligns the shortcuts with the udfps. */
@SysUISingleton
@@ -39,7 +43,8 @@ class ShortcutsBesideUdfpsKeyguardBlueprint
constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultStatusViewSection: DefaultStatusViewSection,
@@ -52,10 +57,9 @@ constructor(
override val id: String = SHORTCUTS_BESIDE_UDFPS
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
- defaultDeviceEntryIconSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection,
defaultStatusViewSection,
@@ -64,6 +68,7 @@ constructor(
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
+ defaultDeviceEntryIconSection, // Add LAST: Intentionally has z-order above other views.
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 35679b84771b..acbcf273214b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -20,18 +20,22 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
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.KeyguardSectionsModule
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
+import javax.inject.Named
/**
* Split-shade layout, mostly used for larger devices like foldables and tablets when in landscape
@@ -45,7 +49,8 @@ constructor(
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
defaultShortcutsSection: DefaultShortcutsSection,
- defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
@@ -58,11 +63,10 @@ constructor(
override val id: String = ID
override val sections =
- listOf(
+ listOfNotNull(
defaultIndicationAreaSection,
- defaultDeviceEntryIconSection,
defaultShortcutsSection,
- defaultAmbientIndicationAreaSection,
+ defaultAmbientIndicationAreaSection.getOrNull(),
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
defaultStatusBarSection,
@@ -71,6 +75,7 @@ constructor(
aodNotificationIconsSection,
aodBurnInSection,
communalTutorialIndicatorSection,
+ defaultDeviceEntryIconSection, // Add LAST: Intentionally has z-order above other views.
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index eb01d4f6f61c..55df46679f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -26,12 +26,13 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
@@ -83,20 +84,27 @@ constructor(
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
+ val lockIconViewId =
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+
constraintSet.apply {
constrainWidth(R.id.start_button, width)
constrainHeight(R.id.start_button, height)
connect(R.id.start_button, LEFT, PARENT_ID, LEFT)
- connect(R.id.start_button, RIGHT, R.id.lock_icon_view, LEFT)
- connect(R.id.start_button, TOP, R.id.lock_icon_view, TOP)
- connect(R.id.start_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+ connect(R.id.start_button, RIGHT, lockIconViewId, LEFT)
+ connect(R.id.start_button, TOP, lockIconViewId, TOP)
+ connect(R.id.start_button, BOTTOM, lockIconViewId, BOTTOM)
constrainWidth(R.id.end_button, width)
constrainHeight(R.id.end_button, height)
connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT)
- connect(R.id.end_button, LEFT, R.id.lock_icon_view, RIGHT)
- connect(R.id.end_button, TOP, R.id.lock_icon_view, TOP)
- connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+ connect(R.id.end_button, LEFT, lockIconViewId, RIGHT)
+ connect(R.id.end_button, TOP, lockIconViewId, TOP)
+ connect(R.id.end_button, BOTTOM, lockIconViewId, BOTTOM)
}
}
}
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 09caf4505c3b..484d351a362e 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,7 @@ import android.view.View
import androidx.constraintlayout.helper.widget.Layer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
import javax.inject.Inject
@@ -33,11 +32,10 @@ class AodBurnInSection
@Inject
constructor(
private val context: Context,
- private val featureFlags: FeatureFlags,
) : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
@@ -53,13 +51,13 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
}
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 975d62a0b9e3..12de185488f7 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
@@ -29,14 +29,15 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -49,8 +50,8 @@ constructor(
private val context: Context,
private val configurationState: ConfigurationState,
private val configurationController: ConfigurationController,
- private val dozeParameters: DozeParameters,
private val featureFlags: FeatureFlagsClassic,
+ private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
private val notificationIconAreaController: NotificationIconAreaController,
@@ -62,7 +63,7 @@ constructor(
private lateinit var nic: NotificationIconContainer
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
nic =
@@ -81,12 +82,11 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
if (NotificationIconContainerRefactor.isEnabled) {
- nic.setOnLockScreen(true)
nicBindingDisposable?.dispose()
nicBindingDisposable =
NotificationIconContainerViewBinder.bind(
@@ -94,6 +94,7 @@ constructor(
nicAodViewModel,
configurationState,
configurationController,
+ iconBindingFailureTracker,
nicAodIconViewStore,
)
} else {
@@ -102,7 +103,7 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
val bottomMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
deleted file mode 100644
index 20cb9b0576db..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout.sections
-
-import android.view.LayoutInflater
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import javax.inject.Inject
-
-class DefaultAmbientIndicationAreaSection
-@Inject
-constructor(
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
- private val keyguardRootViewModel: KeyguardRootViewModel,
-) : KeyguardSection() {
- private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
-
- override fun addViews(constraintLayout: ConstraintLayout) {
- if (keyguardBottomAreaRefactor()) {
- val view =
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.ambient_indication, constraintLayout, false)
-
- constraintLayout.addView(view)
- }
- }
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (keyguardBottomAreaRefactor()) {
- ambientIndicationAreaHandle =
- KeyguardAmbientIndicationAreaViewBinder.bind(
- constraintLayout,
- keyguardAmbientIndicationViewModel,
- keyguardRootViewModel,
- )
- }
- }
-
- override fun applyConstraints(constraintSet: ConstraintSet) {
- constraintSet.apply {
- constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
-
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- // constrain below udfps and above indication area
- constrainHeight(R.id.ambient_indication_container, MATCH_CONSTRAINT)
- connect(R.id.ambient_indication_container, TOP, R.id.lock_icon_view, BOTTOM)
- connect(
- R.id.ambient_indication_container,
- BOTTOM,
- R.id.keyguard_indication_area,
- TOP
- )
- connect(R.id.ambient_indication_container, START, PARENT_ID, START)
- connect(R.id.ambient_indication_container, END, PARENT_ID, END)
- } else {
- // constrain above lock icon
- constrainHeight(R.id.ambient_indication_container, WRAP_CONTENT)
- connect(R.id.ambient_indication_container, BOTTOM, R.id.lock_icon_view, TOP)
- connect(R.id.ambient_indication_container, START, PARENT_ID, START)
- connect(R.id.ambient_indication_container, END, PARENT_ID, END)
- }
- }
- }
-
- override fun removeViews(constraintLayout: ConstraintLayout) {
- ambientIndicationAreaHandle?.destroy()
-
- constraintLayout.removeView(R.id.ambient_indication_container)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index ace970a01054..fac84981577f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -23,6 +23,7 @@ import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
@@ -31,19 +32,28 @@ import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder
import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.NotificationShadeWindowController
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+/** Includes both the device entry icon and the alternate bouncer scrim. */
@ExperimentalCoroutinesApi
class DefaultDeviceEntryIconSection
@Inject
@@ -56,23 +66,33 @@ constructor(
private val featureFlags: FeatureFlags,
private val lockIconViewController: Lazy<LockIconViewController>,
private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+ private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
+ private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
+ private val alternateBouncerViewModel: Lazy<AlternateBouncerViewModel>,
+ private val notificationShadeWindowController: Lazy<NotificationShadeWindowController>,
+ @Application private val scope: CoroutineScope,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
+ private val alternateBouncerViewId = R.id.alternate_bouncer
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!keyguardBottomAreaRefactor() &&
- !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
- ) {
+ if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
return
}
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ // The alternate bouncer scrim needs to be below the device entry icon view, so
+ // we add the view here before adding the device entry icon view.
+ View.inflate(context, R.layout.alternate_bouncer, constraintLayout)
+ }
+
notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
notificationPanelView.removeView(it)
}
val view =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
} else {
// keyguardBottomAreaRefactor()
@@ -82,14 +102,24 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
DeviceEntryIconViewBinder.bind(
it,
deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
)
}
+ constraintLayout.findViewById<FrameLayout?>(alternateBouncerViewId)?.let {
+ AlternateBouncerViewBinder.bind(
+ it,
+ alternateBouncerViewModel.get(),
+ scope,
+ notificationShadeWindowController.get(),
+ )
+ }
} else {
constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
lockIconViewController.get().setLockIconView(it)
@@ -133,7 +163,7 @@ constructor(
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.removeView(deviceEntryIconViewId)
} else {
constraintLayout.removeView(R.id.lock_icon_view)
@@ -153,7 +183,7 @@ constructor(
}
val iconId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
deviceEntryIconViewId
} else {
R.id.lock_icon_view
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 078fefff394a..0588857f408d 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,13 +24,19 @@ 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.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+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
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -39,23 +45,30 @@ class DefaultNotificationStackScrollLayoutSection
@Inject
constructor(
context: Context,
- featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlags,
+ sceneContainerFlags: SceneContainerFlags,
notificationPanelView: NotificationPanelView,
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+ ambientState: AmbientState,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
) :
NotificationStackScrollLayoutSection(
context,
- featureFlags,
+ sceneContainerFlags,
notificationPanelView,
sharedNotificationContainer,
sharedNotificationContainerViewModel,
+ notificationStackAppearanceViewModel,
+ ambientState,
controller,
+ notificationStackSizeCalculator,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
constraintSet.apply {
@@ -78,7 +91,7 @@ constructor(
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
val lockId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
R.id.device_entry_icon_view
} else {
R.id.lock_icon_view
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 0b0f21d7a281..4abcca9d1151 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,9 +31,8 @@ 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.FeatureFlags
-import com.android.systemui.flags.Flags
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.res.R
@@ -49,7 +48,6 @@ class DefaultStatusViewSection
@Inject
constructor(
private val context: Context,
- private val featureFlags: FeatureFlags,
private val notificationPanelView: NotificationPanelView,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
@@ -60,7 +58,7 @@ constructor(
private val statusViewId = R.id.keyguard_status_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
// At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
@@ -82,7 +80,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
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/KeyguardSectionsModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSectionsModule.kt
new file mode 100644
index 000000000000..a65149e6f3aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSectionsModule.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.ui.view.layout.sections
+
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import dagger.BindsOptionalOf
+import dagger.Module
+import javax.inject.Named
+
+@Module
+abstract class KeyguardSectionsModule {
+
+ @Module
+ companion object {
+ const val KEYGUARD_AMBIENT_INDICATION_AREA_SECTION =
+ "keyguard_ambient_indication_area_section"
+ }
+
+ @BindsOptionalOf
+ @Named(KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
+ abstract fun defaultAmbientIndicationAreaSection(): KeyguardSection
+
+} \ No newline at end of file
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 00966f235a57..a9e766e5f98d 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
@@ -21,31 +21,39 @@ import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+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
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
+import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import kotlinx.coroutines.DisposableHandle
abstract class NotificationStackScrollLayoutSection
constructor(
protected val context: Context,
- protected val featureFlags: FeatureFlags,
+ private val sceneContainerFlags: SceneContainerFlags,
private val notificationPanelView: NotificationPanelView,
private val sharedNotificationContainer: SharedNotificationContainer,
private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+ private val ambientState: AmbientState,
private val controller: NotificationStackScrollLayoutController,
+ private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
) : KeyguardSection() {
private val placeHolderId = R.id.nssl_placeholder
private var disposableHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
// This moves the existing NSSL view to a different parent, as the controller is a
@@ -60,7 +68,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
disposableHandle?.dispose()
@@ -68,8 +76,19 @@ constructor(
SharedNotificationContainerBinder.bind(
sharedNotificationContainer,
sharedNotificationContainerViewModel,
+ sceneContainerFlags,
controller,
+ notificationStackSizeCalculator,
)
+ if (sceneContainerFlags.flexiNotifsEnabled()) {
+ NotificationStackAppearanceViewBinder.bind(
+ sharedNotificationContainer,
+ notificationStackAppearanceViewModel,
+ sceneContainerFlags,
+ ambientState,
+ controller,
+ )
+ }
}
override fun removeViews(constraintLayout: ConstraintLayout) {
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 bf95c77229e9..05ef5c386ab1 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
@@ -24,13 +24,19 @@ 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.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+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
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -39,23 +45,30 @@ class SplitShadeNotificationStackScrollLayoutSection
@Inject
constructor(
context: Context,
- featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlags,
+ sceneContainerFlags: SceneContainerFlags,
notificationPanelView: NotificationPanelView,
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+ ambientState: AmbientState,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
) :
NotificationStackScrollLayoutSection(
context,
- featureFlags,
+ sceneContainerFlags,
notificationPanelView,
sharedNotificationContainer,
sharedNotificationContainerViewModel,
+ notificationStackAppearanceViewModel,
+ ambientState,
controller,
+ notificationStackSizeCalculator,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
return
}
constraintSet.apply {
@@ -78,7 +91,7 @@ constructor(
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
val lockId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
R.id.device_entry_icon_view
} else {
R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
new file mode 100644
index 000000000000..4d2af0c7bd4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AodToGoneTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromAodTransitionInteractor.TO_GONE_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE),
+ )
+
+ override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
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 024707ad2885..14de01b41867 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
@@ -17,22 +17,29 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class AodToLockscreenTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
@@ -47,4 +54,21 @@ constructor(
onStart = { 1f },
onStep = { 1f },
)
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ // fade in
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ // background view isn't visible, so return an empty flow
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
new file mode 100644
index 000000000000..06661d0a466b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+
+/** Breaks down AOD->OCCLUDED transition into discrete steps for corresponding views to consume. */
+@SysUISingleton
+class AodToOccludedTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED),
+ )
+
+ override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
new file mode 100644
index 000000000000..3e8bbb3b4c34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon background view. */
+@ExperimentalCoroutinesApi
+class DeviceEntryBackgroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+ lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+ aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
+ occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+ occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+) {
+ private val color: Flow<Int> =
+ configurationRepository.onAnyConfigurationChange
+ .map {
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
+ }
+ .onStart {
+ emit(
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.colorSurface
+ )
+ )
+ }
+ private val alpha: Flow<Float> =
+ setOf(
+ lockscreenToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ aodToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ goneToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ primaryBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ occludedToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ occludedToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ dreamingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ )
+ .merge()
+
+ val viewModel: Flow<BackgroundViewModel> =
+ combine(color, alpha) { color, alpha ->
+ BackgroundViewModel(
+ alpha = alpha,
+ tint = color,
+ )
+ }
+
+ data class BackgroundViewModel(
+ val alpha: Float,
+ val tint: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
new file mode 100644
index 000000000000..99529a100b07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon foreground view (displayed icon). */
+@ExperimentalCoroutinesApi
+class DeviceEntryForegroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ transitionInteractor: KeyguardTransitionInteractor,
+ deviceEntryIconViewModel: DeviceEntryIconViewModel,
+) {
+ private val isShowingAod: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.map { keyguardState ->
+ keyguardState == KeyguardState.AOD
+ }
+ private val color: Flow<Int> =
+ configurationRepository.onAnyConfigurationChange
+ .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) }
+ .onStart {
+ emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
+ }
+ private val useAodIconVariant: Flow<Boolean> =
+ combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) {
+ isTransitionToAod,
+ isUdfps ->
+ isTransitionToAod && isUdfps
+ }
+ .distinctUntilChanged()
+ private val padding: Flow<Int> =
+ configurationRepository.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+
+ val viewModel: Flow<ForegroundIconViewModel> =
+ combine(
+ deviceEntryIconViewModel.iconType,
+ useAodIconVariant,
+ color,
+ padding,
+ ) { iconType, useAodVariant, color, padding ->
+ ForegroundIconViewModel(
+ type = iconType,
+ useAodVariant = useAodVariant,
+ tint = color,
+ padding = padding,
+ )
+ }
+
+ data class ForegroundIconViewModel(
+ val type: DeviceEntryIconView.IconType,
+ val useAodVariant: Boolean,
+ val tint: Int,
+ val padding: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 842dde352c71..5b5a10380a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -12,57 +12,202 @@
* WITHOUT WARRANTIES OR CONDITIONS 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 android.graphics.Color
+import android.animation.FloatEvaluator
+import android.animation.IntEvaluator
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+/** Models the UI state for the containing device entry icon & long-press handling view. */
@ExperimentalCoroutinesApi
-class DeviceEntryIconViewModel @Inject constructor() {
- // TODO: b/305234447 update these states from the data layer
- val iconViewModel: Flow<IconViewModel> =
- flowOf(
- IconViewModel(
- type = DeviceEntryIconView.IconType.LOCK,
- useAodVariant = false,
- tint = Color.WHITE,
- alpha = 1f,
- padding = 48,
+class DeviceEntryIconViewModel
+@Inject
+constructor(
+ transitions: Set<@JvmSuppressWildcards DeviceEntryIconTransition>,
+ burnInInteractor: BurnInInteractor,
+ shadeInteractor: ShadeInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ transitionInteractor: KeyguardTransitionInteractor,
+ val keyguardInteractor: KeyguardInteractor,
+ val viewModel: AodToLockscreenTransitionViewModel,
+ val shadeDependentFlows: ShadeDependentFlows,
+ private val sceneContainerFlags: SceneContainerFlags,
+ private val keyguardViewController: Lazy<KeyguardViewController>,
+ private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
+ udfpsInteractor: DeviceEntryUdfpsInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+) {
+ private val intEvaluator = IntEvaluator()
+ private val floatEvaluator = FloatEvaluator()
+ private val toAodFromState: Flow<KeyguardState> =
+ transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.from }
+ private val showingAlternateBouncer: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.map { keyguardState ->
+ keyguardState == KeyguardState.ALTERNATE_BOUNCER
+ }
+ private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) }
+ private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) }
+ private val transitionAlpha: Flow<Float> =
+ transitions.map { it.deviceEntryParentViewAlpha }.merge()
+ private val alphaMultiplierFromShadeExpansion: Flow<Float> =
+ combine(
+ showingAlternateBouncer,
+ shadeExpansion,
+ qsProgress,
+ ) { showingAltBouncer, shadeExpansion, qsProgress ->
+ val interpolatedQsProgress = (qsProgress * 2).coerceIn(0f, 1f)
+ if (showingAltBouncer) {
+ 1f
+ } else {
+ (1f - shadeExpansion) * (1f - interpolatedQsProgress)
+ }
+ }
+ // Burn-in offsets in AOD
+ private val nonAnimatedBurnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ burnInInteractor.deviceEntryIconXOffset,
+ burnInInteractor.deviceEntryIconYOffset,
+ burnInInteractor.udfpsProgress
+ ) { fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
+ BurnInOffsets(
+ fullyDozingBurnInX,
+ fullyDozingBurnInY,
+ fullyDozingBurnInProgress,
+ )
+ }
+ // Burn-in offsets that animate based on the transition amount to AOD
+ private val animatedBurnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ nonAnimatedBurnInOffsets,
+ transitionInteractor.transitionStepsToState(KeyguardState.AOD)
+ ) { burnInOffsets, transitionStepsToAod ->
+ val dozeAmount = transitionStepsToAod.value
+ BurnInOffsets(
+ intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x),
+ intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y),
+ floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress)
)
- )
- val backgroundViewModel: Flow<BackgroundViewModel> =
- flowOf(BackgroundViewModel(alpha = 1f, tint = Color.GRAY))
- val burnInViewModel: Flow<BurnInViewModel> = flowOf(BurnInViewModel(0, 0, 0f))
- val isLongPressEnabled: Flow<Boolean> = flowOf(true)
+ }
+
+ val deviceEntryViewAlpha: Flow<Float> =
+ combine(
+ transitionAlpha,
+ alphaMultiplierFromShadeExpansion,
+ ) { alpha, alphaMultiplier ->
+ alpha * alphaMultiplier
+ }
+ val useBackgroundProtection: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+ val burnInOffsets: Flow<BurnInOffsets> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+ if (udfpsEnrolled) {
+ toAodFromState.flatMapLatest { fromState ->
+ when (fromState) {
+ KeyguardState.AOD,
+ KeyguardState.GONE,
+ KeyguardState.OCCLUDED,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ KeyguardState.OFF,
+ KeyguardState.DOZING,
+ KeyguardState.DREAMING,
+ KeyguardState.PRIMARY_BOUNCER -> nonAnimatedBurnInOffsets
+ KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
+ KeyguardState.LOCKSCREEN ->
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = nonAnimatedBurnInOffsets,
+ flowWhenShadeIsNotExpanded = animatedBurnInOffsets,
+ )
+ }
+ }
+ } else {
+ // If UDFPS isn't enrolled, we don't show any UI on AOD so there's no need
+ // to use burn in offsets at all
+ flowOf(BurnInOffsets(x = 0, y = 0, progress = 0f))
+ }
+ }
+ val iconType: Flow<DeviceEntryIconView.IconType> =
+ combine(
+ udfpsInteractor.isListeningForUdfps,
+ deviceEntryInteractor.isUnlocked,
+ ) { isListeningForUdfps, isUnlocked ->
+ if (isUnlocked) {
+ DeviceEntryIconView.IconType.UNLOCK
+ } else {
+ if (isListeningForUdfps) {
+ DeviceEntryIconView.IconType.FINGERPRINT
+ } else {
+ DeviceEntryIconView.IconType.LOCK
+ }
+ }
+ }
+ val isLongPressEnabled: Flow<Boolean> =
+ combine(
+ iconType,
+ deviceEntryUdfpsInteractor.isUdfpsSupported,
+ ) { deviceEntryStatus, isUdfps ->
+ when (deviceEntryStatus) {
+ DeviceEntryIconView.IconType.LOCK -> isUdfps
+ DeviceEntryIconView.IconType.UNLOCK -> true
+ DeviceEntryIconView.IconType.FINGERPRINT -> false
+ }
+ }
val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
- flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+ combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
+ if (longPressEnabled) {
+ deviceEntryStatus.toAccessibilityHintType()
+ } else {
+ DeviceEntryIconView.AccessibilityHintType.NONE
+ }
+ }
fun onLongPress() {
- // TODO() vibrate & perform action based on current lock/unlock state
- }
- data class BurnInViewModel(
- val x: Int, // current x burn in offset based on the aodTransitionAmount
- val y: Int, // current y burn in offset based on the aodTransitionAmount
- val progress: Float, // current progress based on the aodTransitionAmount
- )
+ deviceEntryHapticsInteractor.vibrateSuccess()
- class IconViewModel(
- val type: DeviceEntryIconView.IconType,
- val useAodVariant: Boolean,
- val tint: Int,
- val alpha: Float,
- val padding: Int,
- )
+ // TODO (b/309804148): play auth ripple via an interactor
- class BackgroundViewModel(
- val alpha: Float,
- val tint: Int,
- )
+ if (sceneContainerFlags.isEnabled()) {
+ deviceEntryInteractor.attemptDeviceEntry()
+ } else {
+ keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+ }
+ }
+
+ private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
+ DeviceEntryIconView.AccessibilityHintType {
+ return when (this) {
+ DeviceEntryIconView.IconType.LOCK ->
+ DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE
+ DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
+ DeviceEntryIconView.IconType.FINGERPRINT ->
+ DeviceEntryIconView.AccessibilityHintType.NONE
+ }
+ }
}
+
+data class BurnInOffsets(
+ val x: Int, // current x burn in offset based on the aodTransitionAmount
+ val y: Int, // current y burn in offset based on the aodTransitionAmount
+ val progress: Float, // current progress based on the aodTransitionAmount
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
new file mode 100644
index 000000000000..27fb8a3d2473
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down DOZING->LOCKSCREEN transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DozingToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation: KeyguardTransitionAnimationFlow =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ transitionFlow = interactor.dozingToLockscreenTransition,
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index e24d326850e0..a3b8b85fc53d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -18,27 +18,34 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
* consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class DreamingToLockscreenTransitionViewModel
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
-) {
+ private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
+ private val deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
private val transitionAnimation =
@@ -88,4 +95,15 @@ constructor(
duration = 250.milliseconds,
onStep = { it },
)
+
+ val deviceEntryBackgroundViewAlpha =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ // immediately show; will fade in with deviceEntryParentViewAlpha
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+ override val deviceEntryParentViewAlpha = lockscreenAlpha
}
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 601dbccb1de1..62b2281ae473 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
@@ -18,20 +18,27 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
@SysUISingleton
class GoneToAodTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
@@ -60,4 +67,21 @@ constructor(
onStart = { 0f },
onStep = { it },
)
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+ if (udfpsEnrolled) {
+ // fade in at the end of the transition to give time for FP to start running
+ // and avoid a flicker of the unlocked icon
+ transitionAnimation.createFlow(
+ startTime = 1100.milliseconds,
+ duration = 200.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ emptyFlow()
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt
deleted file mode 100644
index dd3967aed99d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardAmbientIndicationViewModel.kt
+++ /dev/null
@@ -1,51 +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.ui.viewmodel
-
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import javax.inject.Inject
-
-class KeyguardAmbientIndicationViewModel
-@Inject
-constructor(
- private val keyguardInteractor: KeyguardInteractor,
- private val burnInHelperWrapper: BurnInHelperWrapper,
-) {
-
- /** An observable for the x-offset by which the indication area should be translated. */
- val indicationAreaTranslationX: Flow<Float> =
- keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
-
- /** Returns an observable for the y-offset by which the indication area should be translated. */
- fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
- return keyguardInteractor.dozeAmount
- .map { dozeAmount ->
- dozeAmount *
- (burnInHelperWrapper.burnInOffset(
- /* amplitude = */ defaultBurnInOffset * 2,
- /* xAxis= */ false,
- ) - defaultBurnInOffset)
- }
- .distinctUntilChanged()
- }
-} \ No newline at end of file
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 60f75f03609c..315626b6fcae 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
@@ -21,11 +21,11 @@ import android.content.Context
import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -49,6 +49,7 @@ import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -99,6 +100,10 @@ constructor(
val goneToAodTransition = keyguardTransitionInteractor.goneToAodTransition
+ /** the shared notification container position *on the lockscreen* */
+ val notificationPositionOnLockscreen: StateFlow<SharedNotificationContainerPosition>
+ get() = keyguardInteractor.sharedNotificationContainerPosition
+
/** An observable for the alpha level for the entire keyguard root view. */
val alpha: Flow<Float> =
previewMode.flatMapLatest {
@@ -249,8 +254,9 @@ constructor(
if (previewMode.value.isInPreviewMode) {
return
}
- keyguardInteractor.sharedNotificationContainerPosition.value =
+ keyguardInteractor.setSharedNotificationContainerPosition(
SharedNotificationContainerPosition(top, bottom)
+ )
}
/** Is there an expanded pulse, are we animating in response? */
@@ -280,7 +286,7 @@ constructor(
dozeParameters.displayNeedsBlanking -> false
// We only want the appear animations to happen when the notifications
// get fully hidden, since otherwise the un-hide animation overlaps.
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
+ newAodTransition() -> true
else -> fullyHidden
}
AnimatableEvent(fullyHidden, animate)
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 c03e4d950cae..539db7fb1ae3 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
@@ -21,6 +21,7 @@ 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.scene.shared.model.SceneKey
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -37,6 +38,8 @@ constructor(
deviceEntryInteractor: DeviceEntryInteractor,
communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
+ val keyguardRoot: KeyguardRootViewModel,
+ val notifications: NotificationsPlaceholderViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: StateFlow<SceneKey> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
new file mode 100644
index 000000000000..2bf12e8e33b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow = interactor.lockscreenToAodTransition,
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ ),
+ )
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = // fade in
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ ),
+ flowWhenShadeIsNotExpanded = transitionAnimation.immediatelyTransitionTo(1f),
+ )
+ } else {
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ flowWhenShadeIsNotExpanded = // fade out
+ transitionAnimation.createFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ onFinish = { 0f },
+ ),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index a3ae67d906bd..52296137a3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -34,7 +35,8 @@ class LockscreenToDreamingTransitionViewModel
@Inject
constructor(
interactor: KeyguardTransitionInteractor,
-) {
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_DREAMING_DURATION,
@@ -60,6 +62,12 @@ constructor(
onStep = { 1f - it },
)
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded = lockscreenAlpha,
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ )
+
companion object {
@JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
}
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
new file mode 100644
index 000000000000..59e5aa845051
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->GONE transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToGoneTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index d3ea89ce1935..d49bc4994b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -33,8 +34,9 @@ import kotlinx.coroutines.flow.Flow
class LockscreenToOccludedTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_OCCLUDED_DURATION,
@@ -59,4 +61,10 @@ constructor(
interpolator = EMPHASIZED_ACCELERATE,
)
}
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded = lockscreenAlpha,
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
new file mode 100644
index 000000000000..f04b67a1d4d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToPrimaryBouncerTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1f - it },
+ onFinish = { 0f }
+ ),
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f)
+ )
+}
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
new file mode 100644
index 000000000000..f7cff9b28542
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/** Breaks down OCCLUDED->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludedToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+ ->
+ if (udfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+}
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 6845c55b8385..0bdc85d05106 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
@@ -18,23 +18,30 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
* consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class OccludedToLockscreenTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_LOCKSCREEN_DURATION,
@@ -58,4 +65,16 @@ constructor(
duration = 250.milliseconds,
onStep = { it },
)
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
new file mode 100644
index 000000000000..05a6d5810be3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down PRIMARY BOUNCER->AOD transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(0f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ emptyFlow()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
new file mode 100644
index 000000000000..3cf793ab9dc8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
new file mode 100644
index 000000000000..e45d537155fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** Helper for flows that depend on the shade expansion */
+class ShadeDependentFlows
+@Inject
+constructor(
+ transitionInteractor: KeyguardTransitionInteractor,
+ shadeInteractor: ShadeInteractor,
+) {
+ /** When the last keyguard state transition started, was the shade fully expanded? */
+ private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded)
+
+ /**
+ * Decide which flow to use depending on the shade expansion state at the start of the last
+ * keyguard state transition.
+ */
+ fun <T> transitionFlow(
+ flowWhenShadeIsExpanded: Flow<T>,
+ flowWhenShadeIsNotExpanded: Flow<T>,
+ ): Flow<T> {
+ val filteredFlowWhenShadeIsExpanded =
+ flowWhenShadeIsExpanded
+ .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+ .filter { (_, shadeFullyExpanded) -> shadeFullyExpanded }
+ .map { (valueWhenShadeIsExpanded, _) -> valueWhenShadeIsExpanded }
+ val filteredFlowWhenShadeIsNotExpanded =
+ flowWhenShadeIsNotExpanded
+ .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+ .filter { (_, shadeFullyExpanded) -> !shadeFullyExpanded }
+ .map { (valueWhenShadeIsNotExpanded, _) -> valueWhenShadeIsNotExpanded }
+ return merge(filteredFlowWhenShadeIsExpanded, filteredFlowWhenShadeIsNotExpanded)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
index c10a4635644f..6e77e13e8513 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.Offsets
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,7 +35,7 @@ constructor(
val context: Context,
) {
val alpha: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
val isVisible: Flow<Boolean> = alpha.map { it != 0f }
// Padding between the fingerprint icon and its bounding box in pixels.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index 0b1079f69d6e..642904df21b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,9 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
import androidx.annotation.ColorInt
import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.Offsets
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -185,7 +185,7 @@ constructor(
keyguardInteractor,
) {
val dozeAmount: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
// Padding between the fingerprint icon and its bounding box in pixels.
val padding: Flow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/util/WallpaperPickerIntentUtils.kt b/packages/SystemUI/src/com/android/systemui/keyguard/util/WallpaperPickerIntentUtils.kt
new file mode 100644
index 000000000000..84e05661d05a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/util/WallpaperPickerIntentUtils.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.keyguard.util
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.res.R
+
+/** Provides function(s) to get intent for launching the Wallpaper Picker app. */
+object WallpaperPickerIntentUtils {
+
+ fun getIntent(context: Context, launchSource: String): Intent {
+ return Intent(Intent.ACTION_SET_WALLPAPER).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ context
+ .getString(R.string.config_wallpaperPickerPackage)
+ .takeIf { it.isNotEmpty() }
+ ?.let { packageName -> setPackage(packageName) }
+ putExtra(WALLPAPER_LAUNCH_SOURCE, launchSource)
+ }
+ }
+
+ private const val WALLPAPER_LAUNCH_SOURCE = "com.android.wallpaper.LAUNCH_SOURCE"
+ const val LAUNCH_SOURCE_KEYGUARD = "app_launched_keyguard"
+}
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 17ff1b1ae888..0d81940cacbd 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -517,6 +517,16 @@ public class LogModule {
}
/**
+ * Provides a {@link LogBuffer} for Scrims like LightRevealScrim.
+ */
+ @Provides
+ @SysUISingleton
+ @ScrimLog
+ public static LogBuffer provideScrimLogBuffer(LogBufferFactory factory) {
+ return factory.create("ScrimLog", 100);
+ }
+
+ /**
* Provides a {@link LogBuffer} for dream-related logs.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt
new file mode 100644
index 000000000000..e78a162e723f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for Scrims like LightRevealScrim */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ScrimLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 2034d97f211c..dcbf670460ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -37,8 +37,6 @@ 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.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
@@ -70,7 +68,6 @@ constructor(
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
dumpManager: DumpManager,
- private val featureFlags: FeatureFlagsClassic,
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -392,13 +389,6 @@ constructor(
)
}
- if (!featureFlags.isEnabled(Flags.MEDIA_DEVICE_NAME_FIX)) {
- if (controller == null || routingSession != null) {
- return routingSession?.name?.toString() ?: device?.name
- }
- return null
- }
-
if (controller == null) {
// In resume state, we don't have a controller - just use the device name
return device?.name
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index b1ff708d020b..9d6e9b4f3ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -46,8 +46,7 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeStateEvents
-import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -103,7 +102,7 @@ constructor(
private val communalInteractor: CommunalInteractor,
configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
- panelEventsEvents: ShadeStateEvents,
+ shadeInteractor: ShadeInteractor,
private val secureSettings: SecureSettings,
@Main private val handler: Handler,
@Application private val coroutineScope: CoroutineScope,
@@ -545,14 +544,12 @@ constructor(
mediaHosts.forEach { it?.updateViewVisibility() }
}
- panelEventsEvents.addShadeStateEventsListener(
- object : ShadeStateEventsListener {
- override fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {
- skipQqsOnExpansion = isExpandImmediateEnabled
- updateDesiredLocation()
- }
+ coroutineScope.launch {
+ shadeInteractor.isQsBypassingShade.collect { isExpandImmediateEnabled ->
+ skipQqsOnExpansion = isExpandImmediateEnabled
+ updateDesiredLocation()
}
- )
+ }
val settingsObserver: ContentObserver =
object : ContentObserver(handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 1ec43c5e3091..d277f32d5e54 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -60,7 +60,7 @@ constructor(
}
companion object {
- @JvmField val GUTS_ANIMATION_DURATION = 500L
+ @JvmField val GUTS_ANIMATION_DURATION = 234L
}
/** A listener when the current dimensions of the player change */
@@ -234,7 +234,8 @@ constructor(
currentStartLocation,
currentEndLocation,
currentTransitionProgress,
- applyImmediately = false
+ applyImmediately = false,
+ isGutsAnimation = true,
)
}
@@ -254,7 +255,8 @@ constructor(
currentStartLocation,
currentEndLocation,
currentTransitionProgress,
- applyImmediately = immediate
+ applyImmediately = immediate,
+ isGutsAnimation = true,
)
}
@@ -414,7 +416,10 @@ constructor(
* it's not available, it will recreate one by measuring, which may be expensive.
*/
@VisibleForTesting
- fun obtainViewState(state: MediaHostState?): TransitionViewState? {
+ fun obtainViewState(
+ state: MediaHostState?,
+ isGutsAnimation: Boolean = false
+ ): TransitionViewState? {
if (state == null || state.measurementInput == null) {
return null
}
@@ -423,7 +428,7 @@ constructor(
val viewState = viewStates[cacheKey]
if (viewState != null) {
// we already have cached this measurement, let's continue
- if (state.squishFraction <= 1f) {
+ if (state.squishFraction <= 1f && !isGutsAnimation) {
return squishViewState(viewState, state.squishFraction)
}
return viewState
@@ -455,13 +460,14 @@ constructor(
// Given that we have a measurement and a view, let's get (guaranteed) viewstates
// from the start and end state and interpolate them
- val startViewState = obtainViewState(startState) as TransitionViewState
+ val startViewState = obtainViewState(startState, isGutsAnimation) as TransitionViewState
val endState = state.copy().also { it.expansion = 1.0f }
- val endViewState = obtainViewState(endState) as TransitionViewState
+ val endViewState = obtainViewState(endState, isGutsAnimation) as TransitionViewState
result =
layoutController.getInterpolatedState(startViewState, endViewState, state.expansion)
}
- if (state.squishFraction <= 1f) {
+ // Skip the adjustments of squish view state if UMO changes due to guts animation.
+ if (state.squishFraction <= 1f && !isGutsAnimation) {
return squishViewState(result, state.squishFraction)
}
return result
@@ -521,7 +527,8 @@ constructor(
@MediaLocation startLocation: Int,
@MediaLocation endLocation: Int,
transitionProgress: Float,
- applyImmediately: Boolean
+ applyImmediately: Boolean,
+ isGutsAnimation: Boolean = false,
) =
traceSection("MediaViewController#setCurrentState") {
currentEndLocation = endLocation
@@ -537,7 +544,7 @@ constructor(
// Obtain the view state that we'd want to be at the end
// The view might not be bound yet or has never been measured and in that case will be
// reset once the state is fully available
- var endViewState = obtainViewState(endHostState) ?: return
+ var endViewState = obtainViewState(endHostState, isGutsAnimation) ?: return
endViewState = updateViewStateSize(endViewState, endLocation, tmpState2)!!
layoutController.setMeasureState(endViewState)
@@ -548,7 +555,7 @@ constructor(
}
val result: TransitionViewState
- var startViewState = obtainViewState(startHostState)
+ var startViewState = obtainViewState(startHostState, isGutsAnimation)
startViewState = updateViewStateSize(startViewState, startLocation, tmpState3)
if (!endHostState.visible) {
@@ -602,7 +609,8 @@ constructor(
applyImmediately,
shouldAnimate,
animationDuration,
- animationDelay
+ animationDelay,
+ isGutsAnimation,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index f2db088ced83..44232ffb3ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -19,12 +19,18 @@ package com.android.systemui.media.controls.util
import android.app.StatusBarManager
import android.os.UserHandle
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
import javax.inject.Inject
@SysUISingleton
-class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+class MediaFlags
+@Inject
+constructor(
+ private val featureFlags: FeatureFlagsClassic,
+ private val sceneContainerFlags: SceneContainerFlags
+) {
/**
* Check whether media control actions should be based on PlaybackState instead of notification
*/
@@ -48,4 +54,8 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
/** Check whether we allow remote media to generate resume controls */
fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
+
+ /** Check whether to use flexiglass layout */
+ fun isFlexiglassEnabled() =
+ sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
new file mode 100644
index 000000000000..898298c58b32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.media.controls.util
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the media_in_scene_container flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object MediaInSceneContainerFlag {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
+
+ /** Is the flag enabled? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.mediaInSceneContainer()
+
+ /**
+ * 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/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index 8453af19d26f..0f54e934f3cf 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -80,12 +80,13 @@ class MediaProjectionPermissionDialogDelegate(
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
+ val singleAppOptionDisabled =
+ appName != null &&
+ mediaProjectionConfig?.regionToCapture ==
+ MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
+
val singleAppDisabledText =
- if (
- appName != null &&
- mediaProjectionConfig?.regionToCapture ==
- MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
- ) {
+ if (singleAppOptionDisabled) {
context.getString(
R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
appName
@@ -93,19 +94,26 @@ class MediaProjectionPermissionDialogDelegate(
} else {
null
}
- return listOf(
- ScreenShareOption(
- mode = SINGLE_APP,
- spinnerText = R.string.screen_share_permission_dialog_option_single_app,
- warningText = singleAppWarningText,
- spinnerDisabledText = singleAppDisabledText,
- ),
- ScreenShareOption(
- mode = ENTIRE_SCREEN,
- spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
- warningText = entireScreenWarningText
+ val options =
+ listOf(
+ ScreenShareOption(
+ mode = SINGLE_APP,
+ spinnerText = R.string.screen_share_permission_dialog_option_single_app,
+ warningText = singleAppWarningText,
+ spinnerDisabledText = singleAppDisabledText,
+ ),
+ ScreenShareOption(
+ mode = ENTIRE_SCREEN,
+ spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
+ warningText = entireScreenWarningText
+ )
)
- )
+ return if (singleAppOptionDisabled) {
+ // Make sure "Entire screen" is the first option when "Single App" is disabled.
+ options.reversed()
+ } else {
+ options
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 79aedffc4498..0320dec0580f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -35,6 +35,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -94,6 +95,9 @@ public class NavigationBarControllerImpl implements
@VisibleForTesting
SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
+ /** Local cache for {@link IWindowManager#hasNavigationBar(int)}. */
+ private SparseBooleanArray mHasNavBar = new SparseBooleanArray();
+
// Tracks config changes that will actually recreate the nav bar
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE
@@ -221,10 +225,16 @@ public class NavigationBarControllerImpl implements
}
private boolean shouldCreateNavBarAndTaskBar(int displayId) {
+ if (mHasNavBar.indexOfKey(displayId) > -1) {
+ return mHasNavBar.get(displayId);
+ }
+
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
try {
- return wms.hasNavigationBar(displayId);
+ boolean hasNavigationBar = wms.hasNavigationBar(displayId);
+ mHasNavBar.put(displayId, hasNavigationBar);
+ return hasNavigationBar;
} catch (RemoteException e) {
// Cannot get wms, just return false with warning message.
Log.w(TAG, "Cannot get WindowManager.");
@@ -268,6 +278,7 @@ public class NavigationBarControllerImpl implements
@Override
public void onDisplayRemoved(int displayId) {
removeNavigationBar(displayId);
+ mHasNavBar.delete(displayId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index dbd62fe16309..d9e3e55c1ad3 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -27,10 +27,10 @@ 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.statusbar.phone.ScreenOffAnimationController
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
/** Hosts business logic for interacting with the power system. */
@SysUISingleton
@@ -59,18 +59,28 @@ constructor(
* Whether we're awake (screen is on and responding to user touch) or asleep (screen is off, or
* on AOD).
*/
- val isAwake = repository.wakefulness
+ val isAwake =
+ repository.wakefulness
.map { it.isAwake() }
.distinctUntilChanged(checkEquivalentUnlessEmitDuplicatesUnderTest)
- /**
- * Helper flow in case "isAsleep" reads better than "!isAwake".
- */
+ /** Helper flow in case "isAsleep" reads better than "!isAwake". */
val isAsleep = isAwake.map { !it }
val screenPowerState = repository.screenPowerState
/**
+ * Notifies the power interactor that a user touch happened.
+ *
+ * @param noChangeLights If true, does not cause the keyboard backlight to turn on because of
+ * this event. This is set when the power key is pressed. We want the device to stay on while
+ * the button is down, but we're about to turn off the screen so we don't want the keyboard
+ * backlight to turn on again. Otherwise the lights flash on and then off and it looks weird.
+ */
+ fun onUserTouch(noChangeLights: Boolean = false) =
+ repository.userTouch(noChangeLights = noChangeLights)
+
+ /**
* Wakes up the device if the device was dozing.
*
* @param why a string explaining why we're waking the device for debugging purposes. Should be
@@ -92,9 +102,7 @@ constructor(
*/
fun wakeUpForFullScreenIntent() {
if (repository.wakefulness.value.isAsleep() || statusBarStateController.isDozing) {
- repository.wakeUp(
- why = FSI_WAKE_WHY,
- wakeReason = PowerManager.WAKE_REASON_APPLICATION)
+ repository.wakeUp(why = FSI_WAKE_WHY, wakeReason = PowerManager.WAKE_REASON_APPLICATION)
}
}
@@ -120,8 +128,8 @@ constructor(
* directly.
*/
fun onStartedWakingUp(
- @PowerManager.WakeReason reason: Int,
- powerButtonLaunchGestureTriggeredOnWakeUp: Boolean,
+ @PowerManager.WakeReason reason: Int,
+ powerButtonLaunchGestureTriggeredOnWakeUp: Boolean,
) {
// If the launch gesture was previously detected, either via onCameraLaunchGestureDetected
// or onFinishedGoingToSleep(), carry that state forward. It will be reset by the next
@@ -210,14 +218,14 @@ constructor(
* reset that flag and then return false.
*/
private val checkEquivalentUnlessEmitDuplicatesUnderTest: (Boolean, Boolean) -> Boolean =
- { old, new ->
- if (emitDuplicateWakefulnessValue) {
- emitDuplicateWakefulnessValue = false
- false
- } else {
- old == new
- }
+ { old, new ->
+ if (emitDuplicateWakefulnessValue) {
+ emitDuplicateWakefulnessValue = false
+ false
+ } else {
+ old == new
}
+ }
/**
* Helper method for tests to simulate the device waking up.
@@ -232,14 +240,14 @@ constructor(
*/
@JvmOverloads
fun PowerInteractor.setAwakeForTest(
- @PowerManager.WakeReason reason: Int = PowerManager.WAKE_REASON_UNKNOWN,
- forceEmit: Boolean = false
+ @PowerManager.WakeReason reason: Int = PowerManager.WAKE_REASON_UNKNOWN,
+ forceEmit: Boolean = false
) {
emitDuplicateWakefulnessValue = forceEmit
this.onStartedWakingUp(
- reason = reason,
- powerButtonLaunchGestureTriggeredOnWakeUp = false,
+ reason = reason,
+ powerButtonLaunchGestureTriggeredOnWakeUp = false,
)
this.onFinishedWakingUp()
}
@@ -258,15 +266,14 @@ constructor(
*/
@JvmOverloads
fun PowerInteractor.setAsleepForTest(
- @PowerManager.GoToSleepReason sleepReason: Int =
- PowerManager.GO_TO_SLEEP_REASON_MIN,
- forceEmit: Boolean = false,
+ @PowerManager.GoToSleepReason sleepReason: Int = PowerManager.GO_TO_SLEEP_REASON_MIN,
+ forceEmit: Boolean = false,
) {
emitDuplicateWakefulnessValue = forceEmit
this.onStartedGoingToSleep(reason = sleepReason)
this.onFinishedGoingToSleep(
- powerButtonLaunchGestureTriggeredDuringSleep = false,
+ powerButtonLaunchGestureTriggeredDuringSleep = false,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index fa18b35b215e..052c0daf0b56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -12,6 +12,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -22,6 +23,7 @@ import android.view.animation.OvershootInterpolator;
import android.widget.Scroller;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -43,6 +45,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private static final int NO_PAGE = -1;
private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
+ private static final int SINGLE_PAGE_SCROLL_DURATION_MILLIS = 300;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
private static final long BOUNCE_ANIMATION_DURATION = 450L;
private static final int TILE_ANIMATION_STAGGER_DELAY = 85;
@@ -63,8 +66,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private PageListener mPageListener;
private boolean mListening;
- private Scroller mScroller;
+ @VisibleForTesting Scroller mScroller;
+ /* set of animations used to indicate which tiles were just revealed */
@Nullable
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
@@ -306,6 +310,38 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
mPageIndicator = indicator;
mPageIndicator.setNumPages(mPages.size());
mPageIndicator.setLocation(mPageIndicatorPosition);
+ mPageIndicator.setOnKeyListener((view, keyCode, keyEvent) -> {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ // only scroll on ACTION_UP as we don't handle longpressing for now. Still we need
+ // to intercept even ACTION_DOWN otherwise keyboard focus will be moved before we
+ // have a chance to intercept ACTION_UP.
+ if (keyEvent.getAction() == KeyEvent.ACTION_UP && mScroller.isFinished()) {
+ scrollByX(getDeltaXForKeyboardScrolling(keyCode),
+ SINGLE_PAGE_SCROLL_DURATION_MILLIS);
+ }
+ return true;
+ }
+ return false;
+ });
+ }
+
+ private int getDeltaXForKeyboardScrolling(int keyCode) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && getCurrentItem() != 0) {
+ return -getWidth();
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ && getCurrentItem() != mPages.size() - 1) {
+ return getWidth();
+ }
+ return 0;
+ }
+
+ private void scrollByX(int x, int durationMillis) {
+ if (x != 0) {
+ mScroller.startScroll(/* startX= */ getScrollX(), /* startY= */ getScrollY(),
+ /* dx= */ x, /* dy= */ 0, /* duration= */ durationMillis);
+ // scroller just sets its state, we need to invalidate view to actually start scrolling
+ postInvalidateOnAnimation();
+ }
}
@Override
@@ -596,9 +632,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
});
setOffscreenPageLimit(lastPageNumber); // Ensure the page to reveal has been inflated.
int dx = getWidth() * lastPageNumber;
- mScroller.startScroll(getScrollX(), getScrollY(), isLayoutRtl() ? -dx : dx, 0,
- REVEAL_SCROLL_DURATION_MILLIS);
- postInvalidateOnAnimation();
+ scrollByX(isLayoutRtl() ? -dx : dx, REVEAL_SCROLL_DURATION_MILLIS);
}
private boolean shouldNotRunAnimation(Set<String> tilesToReveal) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index b0f54ee78482..064423768cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -28,8 +28,8 @@ import android.view.View;
import android.widget.FrameLayout;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
@@ -59,6 +59,8 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private boolean mClippingEnabled;
private boolean mIsFullWidth;
+ private boolean mSceneContainerEnabled;
+
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -72,6 +74,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
+ void setSceneContainerEnabled(boolean enabled) {
+ mSceneContainerEnabled = enabled;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -161,7 +167,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
}
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
- topPadding,
+ mSceneContainerEnabled ? 0 : topPadding,
mQSPanelContainer.getPaddingEnd(),
mQSPanelContainer.getPaddingBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 73a5faabda3b..7b001c7b72f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -24,6 +24,7 @@ import android.view.View;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -44,6 +45,7 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
}
};
+ private final boolean mSceneContainerEnabled;
private final View.OnTouchListener mContainerTouchHandler = new View.OnTouchListener() {
@Override
@@ -64,18 +66,21 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ SceneContainerFlags sceneContainerFlags) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
+ mSceneContainerEnabled = sceneContainerFlags.isEnabled();
}
@Override
public void onInit() {
mQuickStatusBarHeaderController.init();
+ mView.setSceneContainerEnabled(mSceneContainerEnabled);
}
public void setListening(boolean listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 65a2c8ca692b..c77233eb1737 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -35,10 +35,10 @@ import kotlinx.coroutines.launch
/**
* Adapter to determine what real class to use for classes that depend on [QSHost].
- * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is false, all calls will be routed to
+ * * When [QSPipelineFlagsRepository.pipelineEnabled] is false, all calls will be routed to
* [QSTileHost].
- * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is true, calls regarding the current set
- * of tiles will be routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will
+ * * When [QSPipelineFlagsRepository.pipelineEnabled] is true, calls regarding the current set of
+ * tiles will be routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will
* still be routed to [QSTileHost].
*
* This routing also includes dumps.
@@ -60,7 +60,7 @@ constructor(
private const val TAG = "QSTileHost"
}
- private val useNewHost = flags.pipelineHostEnabled
+ private val useNewHost = flags.pipelineEnabled
@GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index fab7e952a874..7f91fd2ebb80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -97,6 +97,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
private boolean mStackScrollerOverscrolling;
private QSAnimator mQSAnimator;
+ @Nullable
private HeightListener mPanelView;
private QSSquishinessController mQSSquishinessController;
protected QuickStatusBarHeader mHeader;
@@ -340,6 +341,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
}
if (mQSCustomizerController != null) {
mQSCustomizerController.setQs(null);
+ mQSCustomizerController.setContainerController(null);
}
mScrollListener = null;
if (mContainer != null) {
@@ -347,6 +349,10 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
}
mDumpManager.unregisterDumpable(getClass().getSimpleName());
mListeningAndVisibilityLifecycleOwner.destroy();
+ ViewGroup parent = ((ViewGroup) getView().getParent());
+ if (parent != null) {
+ parent.removeView(getView());
+ }
}
public void onSaveInstanceState(Bundle outState) {
@@ -853,6 +859,10 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
mQSCustomizerController.hide();
}
+ public void closeCustomizerImmediately() {
+ mQSCustomizerController.hide(false);
+ }
+
public void notifyCustomizeChanged() {
// The customize state changed, so our height changed.
mContainer.updateExpansion();
@@ -863,7 +873,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
- mPanelView.onQsHeightChanged();
+ if (mPanelView != null) {
+ mPanelView.onQsHeightChanged();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 3227f75ed067..ddd7d6781c46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -39,9 +39,9 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -110,6 +110,8 @@ public class QSPanel extends LinearLayout implements Tunable {
*/
private boolean mCanCollapse = true;
+ private boolean mSceneContainerEnabled;
+
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -153,6 +155,13 @@ public class QSPanel extends LinearLayout implements Tunable {
}
}
+ void setSceneContainerEnabled(boolean enabled) {
+ mSceneContainerEnabled = enabled;
+ if (mSceneContainerEnabled) {
+ updatePadding();
+ }
+ }
+
protected void setHorizontalContentContainerClipping() {
mHorizontalContentContainer.setClipChildren(true);
mHorizontalContentContainer.setClipToPadding(false);
@@ -375,7 +384,7 @@ public class QSPanel extends LinearLayout implements Tunable {
int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
setPaddingRelative(getPaddingStart(),
- paddingTop,
+ mSceneContainerEnabled ? 0 : paddingTop,
getPaddingEnd(),
paddingBottom);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 6bbdc54d260d..5eb9620d7334 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -34,6 +34,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -61,6 +62,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private boolean mListening;
+ private final boolean mSceneContainerEnabled;
+
private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
@@ -82,7 +85,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- SplitShadeStateController splitShadeStateController) {
+ SplitShadeStateController splitShadeStateController,
+ SceneContainerFlags sceneContainerFlags) {
super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager, splitShadeStateController);
mTunerService = tunerService;
@@ -96,6 +100,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mSceneContainerEnabled = sceneContainerFlags.isEnabled();
}
@Override
@@ -116,6 +121,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
mView.updateResources();
+ mView.setSceneContainerEnabled(mSceneContainerEnabled);
if (mView.isListening()) {
refreshAllTiles();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 10f95e0b7201..1fab58e18ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -31,7 +31,6 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.ProtoDumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.nano.SystemUIProtoDump;
@@ -49,6 +48,7 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tiles.di.NewQSTileFactory;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -57,8 +57,6 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Lazy;
-
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
@@ -75,6 +73,8 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+
/** Platform implementation of the quick settings tile host
*
* This class keeps track of the set of current tiles and is the in memory source of truth
@@ -167,7 +167,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
// finishes before creating any tiles.
tunerService.addTunable(this, TILES_SETTING);
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
- if (!mFeatureFlags.getPipelineAutoAddEnabled()) {
+ if (!mFeatureFlags.getPipelineEnabled()) {
mAutoTiles = autoTiles.get();
}
});
@@ -288,9 +288,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
}
}
// Do not process tiles if the flag is enabled.
- if (mFeatureFlags.getPipelineHostEnabled()) {
+ if (mFeatureFlags.getPipelineEnabled()) {
return;
}
+ QSPipelineFlagsRepository.Utils.assertInLegacyMode();
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 67c42086364c..c657b55d42d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -40,6 +40,8 @@ public class QuickStatusBarHeader extends FrameLayout {
protected QuickQSPanel mHeaderQsPanel;
+ private boolean mSceneContainerEnabled;
+
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -52,6 +54,13 @@ public class QuickStatusBarHeader extends FrameLayout {
updateResources();
}
+ void setSceneContainerEnabled(boolean enabled) {
+ mSceneContainerEnabled = enabled;
+ if (mSceneContainerEnabled) {
+ updateResources();
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -82,7 +91,9 @@ public class QuickStatusBarHeader extends FrameLayout {
setLayoutParams(lp);
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
- if (largeScreenHeaderActive) {
+ if (mSceneContainerEnabled) {
+ qqsLP.topMargin = 0;
+ } else if (largeScreenHeaderActive) {
qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 64960e6ce23e..1d92d782be69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -29,17 +30,20 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private final QuickQSPanelController mQuickQSPanelController;
private boolean mListening;
+ private final boolean mSceneContainerEnabled;
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- QuickQSPanelController quickQSPanelController
+ QuickQSPanelController quickQSPanelController,
+ SceneContainerFlags sceneContainerFlags
) {
super(view);
mQuickQSPanelController = quickQSPanelController;
+ mSceneContainerEnabled = sceneContainerFlags.isEnabled();
}
-
@Override
protected void onViewAttached() {
+ mView.setSceneContainerEnabled(mSceneContainerEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 024e760e6ed1..c28371ce8aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -244,7 +244,12 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
/** Hice the customizer. */
public void hide() {
- final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
+ hide(true);
+ }
+
+ public void hide(boolean animated) {
+ final boolean animate = animated
+ && mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
if (mView.isShown()) {
mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
mToolbar.dismissPopupMenus();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index b2111d765a9d..496a6f830e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -48,7 +48,7 @@ interface QSHostModule {
qsHost: QSTileHost,
panelInteractorImpl: PanelInteractorImpl
): PanelInteractor {
- return if (featureFlags.pipelineHostEnabled) {
+ return if (featureFlags.pipelineEnabled) {
panelInteractorImpl
} else {
qsHost
@@ -62,7 +62,7 @@ interface QSHostModule {
qsHost: QSTileHost,
customTileAddedRepository: CustomTileAddedSharedPrefsRepository
): CustomTileAddedRepository {
- return if (featureFlags.pipelineHostEnabled) {
+ return if (featureFlags.pipelineEnabled) {
customTileAddedRepository
} else {
qsHost
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index bd4c6e1930ba..1aef9206d99f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -32,6 +32,8 @@ import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.di.QSTilesModule;
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter;
+import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -42,18 +44,19 @@ import com.android.systemui.statusbar.policy.SafetyController;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
-
import java.util.Map;
import javax.inject.Named;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.Multibinds;
+
/**
* Module for QS dependencies
*/
-@Module(subcomponents = {QSFragmentComponent.class, QSFlexiglassComponent.class},
+@Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
includes = {
MediaModule.class,
QSExternalModule.class,
@@ -110,4 +113,7 @@ public interface QSModule {
manager.init();
return manager;
}
+
+ @Binds
+ QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneComponent.kt
index ba1aa629f8cd..b9423683446c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneComponent.kt
@@ -21,12 +21,12 @@ import com.android.systemui.dagger.qualifiers.RootView
import dagger.BindsInstance
import dagger.Subcomponent
-@Subcomponent(modules = [QSFlexiglassModule::class])
+@Subcomponent(modules = [QSSceneModule::class])
@QSScope
-interface QSFlexiglassComponent : QSComponent {
+interface QSSceneComponent : QSComponent {
@Subcomponent.Factory
interface Factory {
- fun create(@RootView @BindsInstance rootView: View): QSFlexiglassComponent
+ fun create(@RootView @BindsInstance rootView: View): QSSceneComponent
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneModule.kt
index 36fac44e0173..446cb62464fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSSceneModule.kt
@@ -24,7 +24,7 @@ import dagger.Provides
import javax.inject.Named
@Module(includes = [QSScopeModule::class])
-interface QSFlexiglassModule {
+interface QSSceneModule {
@Module
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 78f2da53cd43..789a1e401ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -279,6 +279,11 @@ public class TileLifecycleManager extends BroadcastReceiver implements
}
@Override
+ public void onNullBinding(ComponentName name) {
+ executeSetBindService(false);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
handleDeath();
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 4bb8c6e4bb2d..4bda7307c667 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
@@ -20,7 +20,6 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.UserHandle
-import android.util.Log
import com.android.systemui.Dumpable
import com.android.systemui.ProtoDumpable
import com.android.systemui.dagger.SysUISingleton
@@ -174,7 +173,7 @@ constructor(
}
init {
- if (featureFlags.pipelineHostEnabled) {
+ if (featureFlags.pipelineEnabled) {
startTileCollection()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index 1539f05508d0..2930acdeaaa3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -35,7 +35,7 @@ constructor(
) : CoreStartable {
override fun start() {
- if (featureFlags.pipelineAutoAddEnabled) {
+ if (featureFlags.pipelineEnabled) {
autoAddInteractor.init(currentTilesInteractor)
restoreReconciliationInteractor.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 1a71b715fe3a..5c7420cb3c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -1,8 +1,10 @@
package com.android.systemui.qs.pipeline.shared
+import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.RefactorFlagUtils
import javax.inject.Inject
/** Encapsulate the different QS pipeline flags and their dependencies */
@@ -12,16 +14,18 @@ class QSPipelineFlagsRepository
constructor(
private val featureFlags: FeatureFlagsClassic,
) {
-
- /** @see Flags.QS_PIPELINE_NEW_HOST */
- val pipelineHostEnabled: Boolean
- get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)
-
- /** @see Flags.QS_PIPELINE_AUTO_ADD */
- val pipelineAutoAddEnabled: Boolean
- get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
+ val pipelineEnabled: Boolean
+ get() = AconfigFlags.qsNewPipeline()
/** @see Flags.QS_PIPELINE_NEW_TILES */
val pipelineTilesEnabled: Boolean
get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_TILES)
+
+ companion object Utils {
+ fun assertInLegacyMode() =
+ RefactorFlagUtils.assertInLegacyMode(
+ AconfigFlags.qsNewPipeline(),
+ AconfigFlags.FLAG_QS_NEW_PIPELINE
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 2074a14d323f..0a9a6d3c4eba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -33,7 +33,7 @@ import javax.inject.Inject
class QSTileLogger
@Inject
constructor(
- @QSTilesLogBuffers logBuffers: Map<String, LogBuffer>,
+ @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>,
private val factory: LogBufferFactory,
private val mStatusBarStateController: StatusBarStateController,
) {
@@ -162,7 +162,7 @@ constructor(
private fun TileSpec.getLogBuffer(): LogBuffer =
synchronized(logBufferCache) {
- logBufferCache.getOrPut(this.spec) {
+ logBufferCache.getOrPut(this) {
factory.create(
this.getLogTag(),
BUFFER_MAX_SIZE /* maxSize */,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
index 736f7cfbfce9..ae554d954580 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -26,6 +26,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.user.data.repository.UserRepository
@@ -60,12 +61,17 @@ sealed interface QSTileViewModelFactory<T> {
) : QSTileViewModelFactory<T> {
/**
- * Creates [QSTileViewModelImpl] based on the interactors obtained from [component].
- * Reference of that [component] is then stored along the view model.
+ * Creates [QSTileViewModelImpl] based on the interactors obtained from [QSTileComponent].
+ * Reference of that [QSTileComponent] is then stored along the view model.
*/
- fun create(tileSpec: TileSpec, component: QSTileComponent<T>): QSTileViewModelImpl<T> =
- QSTileViewModelImpl(
- qsTileConfigProvider.getConfig(tileSpec.spec),
+ fun create(
+ tileSpec: TileSpec,
+ componentFactory: (config: QSTileConfig) -> QSTileComponent<T>
+ ): QSTileViewModelImpl<T> {
+ val config = qsTileConfigProvider.getConfig(tileSpec.spec)
+ val component = componentFactory(config)
+ return QSTileViewModelImpl(
+ config,
component::userActionInteractor,
component::dataInteractor,
component::dataToStateMapper,
@@ -77,6 +83,7 @@ sealed interface QSTileViewModelFactory<T> {
systemClock,
backgroundDispatcher,
)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 12a083e990f8..5e19439cd643 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -116,7 +116,7 @@ class QSTileViewModelImpl<DATA_TYPE>(
)
override fun forceUpdate() {
- forceUpdates.tryEmit(Unit)
+ tileScope.launch { forceUpdates.emit(Unit) }
}
override fun onUserChanged(user: UserHandle) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 7d7af64a3038..27007bbf7aee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -19,6 +19,11 @@ package com.android.systemui.qs.tiles.di
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
+import com.android.systemui.qs.tiles.impl.custom.di.QSTileConfigModule
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
@@ -34,18 +39,31 @@ constructor(
private val adapterFactory: QSTileViewModelAdapter.Factory,
private val tileMap:
Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+ private val customTileComponentBuilder: CustomTileComponent.Builder,
+ private val customTileViewModelFactory: QSTileViewModelFactory.Component<CustomTileDataModel>,
) : QSFactory {
init {
for (viewModelTileSpec in tileMap.keys) {
- // throws an exception when there is no config for a tileSpec of an injected viewModel
- qsTileConfigProvider.getConfig(viewModelTileSpec)
+ require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) {
+ "No config for $viewModelTileSpec"
+ }
}
}
- override fun createTile(tileSpec: String): QSTile? =
- tileMap[tileSpec]?.let {
- val tile = it.get()
- adapterFactory.create(tile)
+ override fun createTile(tileSpec: String): QSTile? {
+ val viewModel: QSTileViewModel =
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
+ is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
+ is TileSpec.Invalid -> null
+ }
+ ?: return null
+ return adapterFactory.create(viewModel)
+ }
+
+ private fun createCustomTileViewModel(spec: TileSpec.CustomTileSpec): QSTileViewModel =
+ customTileViewModelFactory.create(spec) { config ->
+ customTileComponentBuilder.qsTileConfigModule(QSTileConfigModule(config)).build()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 7b4b55702f55..5bdb592a3558 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -20,9 +20,12 @@ import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
+import android.view.View.AccessibilityDelegate
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
@@ -32,13 +35,18 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.withContext
/** Dialog for showing active, connected and saved bluetooth devices. */
@SysUISingleton
@@ -47,6 +55,7 @@ constructor(
private val bluetoothToggleInitialValue: Boolean,
private val subtitleResIdInitialValue: Int,
private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
@@ -65,13 +74,17 @@ constructor(
private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback)
+ private var lastUiUpdateMs: Long = -1
+
+ private var lastItemRow: Int = -1
+
private lateinit var toggleView: Switch
private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
private lateinit var seeAllViewGroup: View
private lateinit var pairNewDeviceViewGroup: View
- private lateinit var seeAllText: View
- private lateinit var pairNewDeviceText: View
+ private lateinit var seeAllRow: View
+ private lateinit var pairNewDeviceRow: View
private lateinit var deviceListView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
@@ -88,8 +101,8 @@ constructor(
doneButton = requireViewById(R.id.done_button)
seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
- seeAllText = requireViewById(R.id.see_all_text)
- pairNewDeviceText = requireViewById(R.id.pair_new_device_text)
+ seeAllRow = requireViewById(R.id.see_all_clickable_row)
+ pairNewDeviceRow = requireViewById(R.id.pair_new_device_clickable_row)
deviceListView = requireViewById<RecyclerView>(R.id.device_list)
setupToggle()
@@ -97,22 +110,37 @@ constructor(
subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
- seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceText.setOnClickListener {
+ seeAllRow.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceRow.setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
}
- internal fun onDeviceItemUpdated(
+ override fun start() {
+ lastUiUpdateMs = systemClock.elapsedRealtime()
+ }
+
+ internal suspend fun onDeviceItemUpdated(
deviceItem: List<DeviceItem>,
showSeeAll: Boolean,
showPairNewDevice: Boolean
) {
- val start = systemClock.elapsedRealtime()
- deviceItemAdapter.refreshDeviceItemList(deviceItem) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
- logger.logDeviceUiUpdate(systemClock.elapsedRealtime() - start)
+ withContext(mainDispatcher) {
+ val start = systemClock.elapsedRealtime()
+ val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt()
+ // Add a slight delay for smoother dialog height change
+ if (itemRow != lastItemRow) {
+ delay(MIN_HEIGHT_CHANGE_INTERVAL_MS - (start - lastUiUpdateMs))
+ }
+ if (isActive) {
+ deviceItemAdapter.refreshDeviceItemList(deviceItem) {
+ seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ lastUiUpdateMs = systemClock.elapsedRealtime()
+ lastItemRow = itemRow
+ logger.logDeviceUiUpdate(lastUiUpdateMs - start)
+ }
+ }
}
}
@@ -169,7 +197,8 @@ constructor(
deviceItem1.iconWithDescription?.second ==
deviceItem2.iconWithDescription?.second &&
deviceItem1.background == deviceItem2.background &&
- deviceItem1.isEnabled == deviceItem2.isEnabled
+ deviceItem1.isEnabled == deviceItem2.isEnabled &&
+ deviceItem1.actionAccessibilityLabel == deviceItem2.actionAccessibilityLabel
}
}
@@ -213,6 +242,21 @@ constructor(
mutableDeviceItemClick.tryEmit(item)
uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
}
+ accessibilityDelegate =
+ object : AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.addAction(
+ AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ item.actionAccessibilityLabel
+ )
+ )
+ }
+ }
}
nameView.text = item.deviceName
summaryView.text = item.connectionSummary
@@ -230,6 +274,7 @@ constructor(
}
internal companion object {
+ const val MIN_HEIGHT_CHANGE_INTERVAL_MS = 800L
const val MAX_DEVICE_ITEM_ENTRY = 3
const val ACTION_BLUETOOTH_DEVICE_DETAILS =
"com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
@@ -238,5 +283,9 @@ constructor(
const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
const val DISABLED_ALPHA = 0.3f
const val ENABLED_ALPHA = 1f
+
+ private fun Boolean.toInt(): Int {
+ return if (this) 1 else 0
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index f7e0de3ed883..34c2aba1a71f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -40,7 +40,6 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -76,6 +75,7 @@ constructor(
dismissDialog()
var updateDeviceItemJob: Job? = null
+ var updateDialogUiJob: Job? = null
job =
coroutineScope.launch(mainDispatcher) {
@@ -93,10 +93,9 @@ constructor(
)
}
?: dialog!!.show()
+
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
- // Add a slight delay for smoother dialog bounds change
- delay(FIRST_LOAD_DELAY_MS)
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
@@ -128,11 +127,14 @@ constructor(
deviceItemInteractor.deviceItemUpdate
.onEach {
- dialog!!.onDeviceItemUpdated(
- it.take(MAX_DEVICE_ITEM_ENTRY),
- showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
- showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
- )
+ updateDialogUiJob?.cancel()
+ updateDialogUiJob = launch {
+ dialog?.onDeviceItemUpdated(
+ it.take(MAX_DEVICE_ITEM_ENTRY),
+ showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
+ showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
+ )
+ }
}
.launchIn(this)
@@ -153,6 +155,7 @@ constructor(
bluetoothStateInteractor.isBluetoothEnabled,
getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled),
this@BluetoothTileDialogViewModel,
+ mainDispatcher,
systemClock,
uiEventLogger,
logger,
@@ -205,7 +208,6 @@ constructor(
companion object {
private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
- private const val FIRST_LOAD_DELAY_MS = 500L
private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
else R.string.bt_is_off
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
index 2c8d2a0806d2..1c621b87533d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
@@ -49,5 +49,6 @@ data class DeviceItem(
val connectionSummary: String = "",
val iconWithDescription: Pair<Drawable, String>? = null,
val background: Int? = null,
- var isEnabled: Boolean = true
+ var isEnabled: Boolean = true,
+ var actionAccessibilityLabel: String = "",
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
index 7bb1619c5001..1c9be0f105b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -28,6 +28,10 @@ private val backgroundOff = R.drawable.bluetooth_tile_dialog_bg_off
private val backgroundOffBusy = R.drawable.bluetooth_tile_dialog_bg_off_busy
private val connected = R.string.quick_settings_bluetooth_device_connected
private val saved = R.string.quick_settings_bluetooth_device_saved
+private val actionAccessibilityLabelActivate =
+ R.string.accessibility_quick_settings_bluetooth_device_tap_to_activate
+private val actionAccessibilityLabelDisconnect =
+ R.string.accessibility_quick_settings_bluetooth_device_tap_to_disconnect
/** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */
internal abstract class DeviceItemFactory {
@@ -60,6 +64,7 @@ internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() {
},
background = backgroundOn,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
)
}
}
@@ -87,6 +92,7 @@ internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
)
}
}
@@ -112,6 +118,7 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
)
}
}
@@ -137,6 +144,7 @@ internal class SavedDeviceItemFactory : DeviceItemFactory() {
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
index 761274e96e43..14bf25d10d88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
@@ -19,17 +19,18 @@ package com.android.systemui.qs.tiles.impl.custom
import android.os.UserHandle
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@QSTileScope
-class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileData> {
+class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileDataModel> {
override fun tileData(
user: UserHandle,
triggers: Flow<DataUpdateTrigger>
- ): Flow<CustomTileData> {
+ ): Flow<CustomTileDataModel> {
TODO("Not yet implemented")
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
index f7bec024b7bb..e23a5c2f0b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
@@ -17,15 +17,16 @@
package com.android.systemui.qs.tiles.impl.custom
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import javax.inject.Inject
@QSTileScope
-class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileData> {
+class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileDataModel> {
- override fun map(config: QSTileConfig, data: CustomTileData): QSTileState {
+ override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
index 6c1c1a34abc0..f34704be8bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
@@ -18,14 +18,15 @@ package com.android.systemui.qs.tiles.impl.custom
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
@QSTileScope
class CustomTileUserActionInteractor @Inject constructor() :
- QSTileUserActionInteractor<CustomTileData> {
+ QSTileUserActionInteractor<CustomTileDataModel> {
- override suspend fun handleInput(input: QSTileInput<CustomTileData>) {
+ override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
index 01df90662579..88bc8fa81e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -16,13 +16,14 @@
package com.android.systemui.qs.tiles.impl.custom.di
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import dagger.Subcomponent
@QSTileScope
@Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
-interface CustomTileComponent : QSTileComponent<Any> {
+interface CustomTileComponent : QSTileComponent<CustomTileDataModel> {
@Subcomponent.Builder
interface Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 482bf9bcd051..83767aa9d444 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -19,13 +19,13 @@ package com.android.systemui.qs.tiles.impl.custom.di
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.custom.CustomTileData
import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
import dagger.Module
@@ -36,15 +36,15 @@ interface CustomTileModule {
@Binds
fun bindDataInteractor(
dataInteractor: CustomTileInteractor
- ): QSTileDataInteractor<CustomTileData>
+ ): QSTileDataInteractor<CustomTileDataModel>
@Binds
fun bindUserActionInteractor(
userActionInteractor: CustomTileUserActionInteractor
- ): QSTileUserActionInteractor<CustomTileData>
+ ): QSTileUserActionInteractor<CustomTileDataModel>
@Binds
- fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData>
+ fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileDataModel>
@Binds
fun bindCustomTileDefaultsRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
index bb5a229a0696..f095c01126c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.domain.entity
import android.content.ComponentName
import android.graphics.drawable.Icon
@@ -22,12 +22,11 @@ import android.os.UserHandle
import android.service.quicksettings.Tile
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
-data class CustomTileData(
+data class CustomTileDataModel(
val user: UserHandle,
val componentName: ComponentName,
val tile: Tile,
val callingAppUid: Int,
- val isActive: Boolean,
val hasPendingBind: Boolean,
val shouldShowChevron: Boolean,
val defaultTileLabel: CharSequence?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
new file mode 100644
index 000000000000..b2b226464ee5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.flashlight.domain
+
+import android.content.Context
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [FlashlightTileModel] to [QSTileState]. */
+class FlashlightMapper @Inject constructor(private val context: Context) :
+ QSTileDataToStateMapper<FlashlightTileModel> {
+
+ override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
+ QSTileState.build(context, config.uiConfig) {
+ val icon =
+ Icon.Loaded(
+ context.resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ }
+ ),
+ contentDescription = null
+ )
+ this.icon = { icon }
+
+ if (data.isEnabled) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[2]
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[1]
+ }
+ contentDescription = label
+ supportedActions =
+ setOf(
+ QSTileState.UserAction.CLICK,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
new file mode 100644
index 000000000000..53d4cf96809c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.flashlight.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.statusbar.policy.FlashlightController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Observes flashlight state changes providing the [FlashlightTileModel]. */
+class FlashlightTileDataInteractor
+@Inject
+constructor(
+ private val flashlightController: FlashlightController,
+) : QSTileDataInteractor<FlashlightTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<FlashlightTileModel> = conflatedCallbackFlow {
+ val initialValue = flashlightController.isEnabled
+ trySend(FlashlightTileModel(initialValue))
+
+ val callback =
+ object : FlashlightController.FlashlightListener {
+ override fun onFlashlightChanged(enabled: Boolean) {
+ trySend(FlashlightTileModel(enabled))
+ }
+ override fun onFlashlightError() {
+ trySend(FlashlightTileModel(false))
+ }
+ override fun onFlashlightAvailabilityChanged(available: Boolean) {
+ trySend(FlashlightTileModel(flashlightController.isEnabled))
+ }
+ }
+ flashlightController.addCallback(callback)
+ awaitClose { flashlightController.removeCallback(callback) }
+ }
+
+ override fun availability(user: UserHandle): Flow<Boolean> =
+ flowOf(flashlightController.hasFlashlight())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
new file mode 100644
index 000000000000..9180e3080d07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.flashlight.domain.interactor
+
+import android.app.ActivityManager
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.statusbar.policy.FlashlightController
+import javax.inject.Inject
+
+/** Handles flashlight tile clicks. */
+class FlashlightTileUserActionInteractor
+@Inject
+constructor(
+ private val flashlightController: FlashlightController,
+) : QSTileUserActionInteractor<FlashlightTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<FlashlightTileModel>) =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ if (!ActivityManager.isUserAMonkey()) {
+ flashlightController.setFlashlight(!input.data.isEnabled)
+ }
+ }
+ else -> {}
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/model/FlashlightTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/model/FlashlightTileModel.kt
new file mode 100644
index 000000000000..ef6b2be06747
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/model/FlashlightTileModel.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.qs.tiles.impl.flashlight.domain.model
+
+/**
+ * Flashlight tile model.
+ *
+ * @param isEnabled is true when the falshlight is enabled;
+ */
+@JvmInline value class FlashlightTileModel(val isEnabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
index 3f3b94e65294..0609e797d53b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -18,20 +18,31 @@ package com.android.systemui.qs.tiles.viewmodel
import com.android.internal.util.Preconditions
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
interface QSTileConfigProvider {
/**
- * Returns a [QSTileConfig] for a [tileSpec] or throws [IllegalArgumentException] if there is no
- * config for such [tileSpec].
+ * Returns a [QSTileConfig] for a [tileSpec]:
+ * - injected config for [TileSpec.PlatformTileSpec] or throws [IllegalArgumentException] if
+ * there is none
+ * - new config for [TileSpec.CustomTileSpec].
+ * - throws [IllegalArgumentException] for [TileSpec.Invalid]
*/
fun getConfig(tileSpec: String): QSTileConfig
+
+ fun hasConfig(tileSpec: String): Boolean
}
@SysUISingleton
-class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<String, QSTileConfig>) :
- QSTileConfigProvider {
+class QSTileConfigProviderImpl
+@Inject
+constructor(
+ private val configs: Map<String, QSTileConfig>,
+ private val qsEventLogger: QsEventLogger,
+) : QSTileConfigProvider {
init {
for (entry in configs.entries) {
@@ -44,6 +55,26 @@ class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<Stri
}
}
+ override fun hasConfig(tileSpec: String): Boolean =
+ when (TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> configs.containsKey(tileSpec)
+ is TileSpec.CustomTileSpec -> true
+ is TileSpec.Invalid -> false
+ }
+
override fun getConfig(tileSpec: String): QSTileConfig =
- configs[tileSpec] ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> {
+ configs[tileSpec]
+ ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ }
+ is TileSpec.CustomTileSpec ->
+ QSTileConfig(
+ spec,
+ QSTileUIConfig.Empty,
+ qsEventLogger.getNewInstanceId(),
+ )
+ is TileSpec.Invalid ->
+ throw IllegalArgumentException("TileSpec.Invalid doesn't support configs")
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 30b87cc9e662..f9e0b160acd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -18,7 +18,10 @@ package com.android.systemui.qs.tiles.viewmodel
import android.content.Context
import android.service.quicksettings.Tile
+import android.view.View
+import android.widget.Switch
import com.android.systemui.common.shared.model.Icon
+import kotlin.reflect.KClass
/**
* Represents current a state of the tile to be displayed in on the view. Consider using
@@ -111,7 +114,7 @@ data class QSTileState(
var stateDescription: CharSequence? = null
var sideViewIcon: SideViewIcon = SideViewIcon.None
var enabledState: EnabledState = EnabledState.ENABLED
- var expandedAccessibilityClassName: String? = null
+ var expandedAccessibilityClass: KClass<out View>? = Switch::class
fun build(): QSTileState =
QSTileState(
@@ -124,7 +127,7 @@ data class QSTileState(
stateDescription,
sideViewIcon,
enabledState,
- expandedAccessibilityClassName,
+ expandedAccessibilityClass?.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 771d07c35cc3..3afbd7c0d4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -234,6 +234,9 @@ constructor(
disabledByPolicy = viewModelState.enabledState == QSTileState.EnabledState.DISABLED
expandedAccessibilityClassName = viewModelState.expandedAccessibilityClassName
+ // Use LoopedAnimatable2DrawableWrapper to achieve animated tile icon
+ isTransient = false
+
when (viewModelState.sideViewIcon) {
is QSTileState.SideViewIcon.Custom -> {
sideViewCustomDrawable =
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
new file mode 100644
index 000000000000..b4340f5d59d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.ui.adapter
+
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.VisibleForTesting
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.QSImpl
+import com.android.systemui.qs.dagger.QSSceneComponent
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+// TODO(307945185) Split View concerns into a ViewBinder
+/** Adapter to use between Scene system and [QSImpl] */
+interface QSSceneAdapter {
+ /** Whether [QSImpl] is currently customizing */
+ val isCustomizing: StateFlow<Boolean>
+
+ /**
+ * A view with the QS content ([QSContainerImpl]), managed by an instance of [QSImpl] tracked by
+ * the interactor.
+ */
+ val qsView: Flow<View>
+
+ /**
+ * Inflate an instance of [QSImpl] for this context. Once inflated, it will be available in
+ * [qsView]
+ */
+ suspend fun inflate(context: Context, parent: ViewGroup? = null)
+
+ /** Set the current state for QS. [state] must not be [State.INITIAL]. */
+ fun setState(state: State)
+
+ sealed class State(
+ val isVisible: Boolean,
+ val expansion: Float,
+ ) {
+ data object CLOSED : State(false, 0f)
+ data object QQS : State(true, 0f)
+ data object QS : State(true, 1f)
+ }
+}
+
+@SysUISingleton
+class QSSceneAdapterImpl
+@VisibleForTesting
+constructor(
+ private val qsSceneComponentFactory: QSSceneComponent.Factory,
+ private val qsImplProvider: Provider<QSImpl>,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ @Application applicationScope: CoroutineScope,
+ private val asyncLayoutInflaterFactory: (Context) -> AsyncLayoutInflater,
+) : QSContainerController, QSSceneAdapter {
+
+ @Inject
+ constructor(
+ qsSceneComponentFactory: QSSceneComponent.Factory,
+ qsImplProvider: Provider<QSImpl>,
+ @Main dispatcher: CoroutineDispatcher,
+ @Application scope: CoroutineScope,
+ ) : this(qsSceneComponentFactory, qsImplProvider, dispatcher, scope, ::AsyncLayoutInflater)
+
+ private val state = MutableStateFlow<QSSceneAdapter.State>(QSSceneAdapter.State.CLOSED)
+ private val _isCustomizing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isCustomizing = _isCustomizing.asStateFlow()
+
+ private val _qsImpl: MutableStateFlow<QSImpl?> = MutableStateFlow(null)
+ val qsImpl = _qsImpl.asStateFlow()
+ override val qsView: Flow<View> = _qsImpl.map { it?.view }.filterNotNull()
+
+ init {
+ applicationScope.launch {
+ state.sample(_isCustomizing, ::Pair).collect { (state, customizing) ->
+ _qsImpl.value?.apply {
+ if (state != QSSceneAdapter.State.QS && customizing) {
+ this@apply.closeCustomizerImmediately()
+ }
+ applyState(state)
+ }
+ }
+ }
+ }
+
+ override fun setCustomizerAnimating(animating: Boolean) {}
+
+ override fun setCustomizerShowing(showing: Boolean) {
+ _isCustomizing.value = showing
+ }
+
+ override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) {
+ setCustomizerShowing(showing)
+ }
+
+ override fun setDetailShowing(showing: Boolean) {}
+
+ override suspend fun inflate(context: Context, parent: ViewGroup?) {
+ withContext(mainDispatcher) {
+ val inflater = asyncLayoutInflaterFactory(context)
+ val view = suspendCoroutine { continuation ->
+ inflater.inflate(R.layout.qs_panel, parent) { view, _, _ ->
+ continuation.resume(view)
+ }
+ }
+ val bundle = Bundle()
+ _qsImpl.value?.onSaveInstanceState(bundle)
+ _qsImpl.value?.onDestroy()
+ val component = qsSceneComponentFactory.create(view)
+ val qs = qsImplProvider.get()
+ qs.onCreate(null)
+ qs.onComponentCreated(component, bundle)
+ _qsImpl.value = qs
+ qs.view.setPadding(0, 0, 0, 0)
+ qs.setContainerController(this@QSSceneAdapterImpl)
+ qs.applyState(state.value)
+ }
+ }
+ override fun setState(state: QSSceneAdapter.State) {
+ this.state.value = state
+ }
+
+ private fun QSImpl.applyState(state: QSSceneAdapter.State) {
+ setQsVisible(state.isVisible)
+ setExpanded(state.isVisible)
+ setListening(state.isVisible)
+ setQsExpansion(state.expansion, 1f, 0f, 1f)
+ setTransitionToFullShadeProgress(false, 1f, 1f)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 9edd2c6cf927..e5e1e8445e94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -16,21 +16,35 @@
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
+import kotlinx.coroutines.flow.map
/** Models UI state and handles user input for the quick settings scene. */
@SysUISingleton
class QuickSettingsSceneViewModel
@Inject
constructor(
- private val bouncerInteractor: BouncerInteractor,
val shadeHeaderViewModel: ShadeHeaderViewModel,
+ val qsSceneAdapter: QSSceneAdapter,
+ val notifications: NotificationsPlaceholderViewModel,
) {
- /** Notifies that some content in quick settings was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
+ val destinationScenes =
+ qsSceneAdapter.isCustomizing.map { customizing ->
+ if (customizing) {
+ mapOf<UserAction, SceneModel>(UserAction.Back to SceneModel(SceneKey.QuickSettings))
+ } else {
+ mapOf(
+ UserAction.Back to SceneModel(SceneKey.Shade),
+ UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
+ )
+ }
+ }
}
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 b1440031a2a0..0abde4d5c3f4 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
@@ -18,7 +18,7 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.power.data.repository.PowerRepository
+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
@@ -48,7 +48,7 @@ class SceneInteractor
constructor(
@Application private val applicationScope: CoroutineScope,
private val repository: SceneContainerRepository,
- private val powerRepository: PowerRepository,
+ private val powerInteractor: PowerInteractor,
private val logger: SceneLogger,
) {
@@ -202,7 +202,7 @@ constructor(
/** Handles a user input event. */
fun onUserInput() {
- powerRepository.userTouch()
+ 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 91b4d1778e1c..f3f9c916d705 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
@@ -20,7 +20,9 @@ package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
@@ -45,6 +47,7 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_B
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
@@ -64,7 +67,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
private val deviceEntryInteractor: DeviceEntryInteractor,
- private val authenticationInteractor: AuthenticationInteractor,
+ private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val flags: SceneContainerFlags,
private val sysUiState: SysUiState,
@@ -72,6 +75,8 @@ constructor(
private val sceneLogger: SceneLogger,
@FalsingCollectorActual private val falsingCollector: FalsingCollector,
private val powerInteractor: PowerInteractor,
+ private val simBouncerInteractor: SimBouncerInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
) : CoreStartable {
override fun start() {
@@ -92,6 +97,7 @@ constructor(
/** Updates the visibility of the scene container. */
private fun hydrateVisibility() {
applicationScope.launch {
+ // TODO(b/296114544): Combine with some global hun state to make it visible!
sceneInteractor.transitionState
.mapNotNull { state ->
when (state) {
@@ -121,6 +127,44 @@ constructor(
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
applicationScope.launch {
+ // TODO (b/308001302): Move this to a bouncer specific interactor.
+ bouncerInteractor.onImeHidden.collectLatest {
+ if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Lockscreen),
+ loggingReason = "IME hidden",
+ )
+ }
+ }
+ }
+ applicationScope.launch {
+ simBouncerInteractor.isAnySimSecure.collect { isAnySimLocked ->
+ val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
+ val isUnlocked = deviceEntryInteractor.isUnlocked.value
+
+ when {
+ isAnySimLocked -> {
+ switchToScene(
+ targetSceneKey = SceneKey.Bouncer,
+ loggingReason = "Need to authenticate locked sim card."
+ )
+ }
+ isUnlocked && !canSwipeToEnter -> {
+ switchToScene(
+ targetSceneKey = SceneKey.Gone,
+ loggingReason = "Sim cards are unlocked."
+ )
+ }
+ else -> {
+ switchToScene(
+ targetSceneKey = SceneKey.Lockscreen,
+ loggingReason = "Sim cards are unlocked."
+ )
+ }
+ }
+ }
+ }
+ applicationScope.launch {
deviceEntryInteractor.isUnlocked
.mapNotNull { isUnlocked ->
val renderedScenes =
@@ -132,41 +176,41 @@ constructor(
transitionState.toScene,
)
}
- when {
- isUnlocked ->
- when {
- // When the device becomes unlocked in Bouncer, go to Gone.
- renderedScenes.contains(SceneKey.Bouncer) ->
- SceneKey.Gone to "device unlocked in Bouncer scene"
-
- // When the device becomes unlocked in Lockscreen, go to Gone if
- // bypass is enabled.
- renderedScenes.contains(SceneKey.Lockscreen) ->
- if (deviceEntryInteractor.isBypassEnabled.value) {
- SceneKey.Gone to
- "device unlocked in Lockscreen scene with bypass"
- } else {
- null
- }
-
- // We got unlocked while on a scene that's not Lockscreen or
- // Bouncer, no need to change scenes.
- else -> null
- }
-
- // When the device becomes locked, to Lockscreen.
- !isUnlocked ->
- when {
- // Already on lockscreen or bouncer, no need to change scenes.
- renderedScenes.contains(SceneKey.Lockscreen) ||
- renderedScenes.contains(SceneKey.Bouncer) -> null
+ val isOnLockscreen = renderedScenes.contains(SceneKey.Lockscreen)
+ val isOnBouncer = renderedScenes.contains(SceneKey.Bouncer)
+ if (!isUnlocked) {
+ return@mapNotNull if (isOnLockscreen || isOnBouncer) {
+ // Already on lockscreen or bouncer, no need to change scenes.
+ null
+ } else {
+ // The device locked while on a scene that's not Lockscreen or Bouncer,
+ // go to Lockscreen.
+ SceneKey.Lockscreen to
+ "device locked in non-Lockscreen and non-Bouncer scene"
+ }
+ }
- // We got locked while on a scene that's not Lockscreen or Bouncer,
- // go to Lockscreen.
- else ->
- SceneKey.Lockscreen to
- "device locked in non-Lockscreen and non-Bouncer scene"
- }
+ val isBypassEnabled = deviceEntryInteractor.isBypassEnabled.value
+ val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
+ when {
+ isOnBouncer ->
+ // When the device becomes unlocked in Bouncer, go to Gone.
+ SceneKey.Gone to "device was unlocked in Bouncer scene"
+ isOnLockscreen ->
+ // The lockscreen should be dismissed automatically in 2 scenarios:
+ // 1. When face auth bypass is enabled and authentication happens while
+ // the user is on the lockscreen.
+ // 2. Whenever the user authenticates using an active authentication
+ // mechanism like fingerprint auth. Since canSwipeToEnter is true
+ // when the user is passively authenticated, the false value here
+ // when the unlock state changes indicates this is an active
+ // authentication attempt.
+ if (isBypassEnabled || !canSwipeToEnter)
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with either " +
+ "bypass enabled or using an active authentication mechanism"
+ else null
+ // Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
}
@@ -186,24 +230,23 @@ constructor(
loggingReason = "device is starting to sleep",
)
} else {
- val authMethod = authenticationInteractor.getAuthenticationMethod()
+ val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- when {
- authMethod == AuthenticationMethodModel.None -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while auth method is" + " none",
- )
- }
- authMethod.isSecure && isUnlocked -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while unlocked with a" +
- " secure auth method",
- )
- }
+ if (isUnlocked && !canSwipeToEnter) {
+ switchToScene(
+ targetSceneKey = SceneKey.Gone,
+ loggingReason =
+ "device is waking up while unlocked without the ability" +
+ " to swipe up on lockscreen to enter.",
+ )
+ } else if (
+ authenticationInteractor.getAuthenticationMethod() ==
+ AuthenticationMethodModel.Sim
+ ) {
+ switchToScene(
+ targetSceneKey = SceneKey.Bouncer,
+ loggingReason = "device is starting to wake up with a locked sim"
+ )
}
}
}
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 e40d2b7fd659..d14ef35027a3 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
@@ -17,8 +17,8 @@
package com.android.systemui.scene.shared.flag
import androidx.annotation.VisibleForTesting
-import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
@@ -28,6 +28,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.flags.ReleasedFlag
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -58,8 +59,6 @@ constructor(
@VisibleForTesting
val classicFlagTokens: List<Flag<Boolean>> =
listOf(
- Flags.MIGRATE_NSSL,
- Flags.MIGRATE_KEYGUARD_STATUS_VIEW,
Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW,
)
}
@@ -75,6 +74,10 @@ constructor(
flagName = AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
flagValue = keyguardBottomAreaRefactor(),
),
+ AconfigFlagMustBeEnabled(
+ flagName = KeyguardShadeMigrationNssl.FLAG_NAME,
+ flagValue = KeyguardShadeMigrationNssl.isEnabled,
+ ),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index 7fc409445f50..c88a04c24044 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -4,9 +4,11 @@ import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.WindowInsets
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import kotlinx.coroutines.flow.MutableStateFlow
/** A root view of the main SysUI window that supports scenes. */
@@ -27,6 +29,8 @@ class SceneWindowRootView(
fun init(
viewModel: SceneContainerViewModel,
containerConfig: SceneContainerConfig,
+ sharedNotificationContainer: SharedNotificationContainer,
+ flags: SceneContainerFlags,
scenes: Set<Scene>,
layoutInsetController: LayoutInsetsController,
) {
@@ -37,6 +41,8 @@ class SceneWindowRootView(
viewModel = viewModel,
windowInsets = windowInsets,
containerConfig = containerConfig,
+ sharedNotificationContainer = sharedNotificationContainer,
+ flags = flags,
scenes = scenes,
onVisibilityChangedInternal = { isVisible ->
super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 17d6c9d319e8..4a839b8df9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -31,10 +31,13 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import java.time.Instant
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@@ -47,6 +50,8 @@ object SceneWindowRootViewBinder {
viewModel: SceneContainerViewModel,
windowInsets: StateFlow<WindowInsets?>,
containerConfig: SceneContainerConfig,
+ sharedNotificationContainer: SharedNotificationContainer,
+ flags: SceneContainerFlags,
scenes: Set<Scene>,
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
) {
@@ -91,6 +96,13 @@ object SceneWindowRootViewBinder {
val legacyView = view.requireViewById<View>(R.id.legacy_window_root)
view.addView(createVisibilityToggleView(legacyView))
+ if (flags.flexiNotifsEnabled()) {
+ (sharedNotificationContainer.parent as? ViewGroup)?.removeView(
+ sharedNotificationContainer
+ )
+ view.addView(sharedNotificationContainer)
+ }
+
launch {
viewModel.isVisible.collect { isVisible ->
onVisibilityChangedInternal(isVisible)
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index f4d19dc2691d..d0585d3782c2 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -86,6 +86,8 @@ public class ScrimView extends View {
public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setFocusable(false);
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mDrawable = new ScrimDrawable();
mDrawable.setCallback(this);
mColors = new ColorExtractor.GradientColors();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index ead10d6da860..9f4ea27b9ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -181,12 +181,16 @@ public class BrightnessDialog extends Activity {
if (mCancelTimeoutRunnable != null) {
mCancelTimeoutRunnable.run();
}
- finish();
+ requestFinish();
}
return super.onKeyDown(keyCode, event);
}
+ protected void requestFinish() {
+ finish();
+ }
+
private boolean triggeredByBrightnessKey() {
return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false);
}
@@ -197,6 +201,6 @@ public class BrightnessDialog extends Activity {
}
final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
- mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout);
+ mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::requestFinish, timeout);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index b30bc56aeeb0..bc5090f14d23 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,12 +30,11 @@ import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -282,7 +281,6 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
- private final ActivityStarter mActivityStarter;
@Inject
public Factory(
@@ -290,14 +288,13 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
UiEventLogger uiEventLogger,
VibratorHelper vibratorHelper,
SystemClock clock,
- @Main CoroutineDispatcher mainDispatcher,
- ActivityStarter activityStarter) {
+ @Main CoroutineDispatcher mainDispatcher
+ ) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
- mActivityStarter = activityStarter;
}
/**
@@ -313,8 +310,6 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
- root.setActivityStarter(mActivityStarter);
-
BrightnessSliderHapticPlugin plugin;
if (hapticBrightnessSlider()) {
plugin = new BrightnessSliderHapticPluginImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 5ecf07f5a264..c88549224183 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -33,7 +33,6 @@ import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
/**
@@ -42,7 +41,6 @@ import com.android.systemui.res.R;
*/
public class BrightnessSliderView extends FrameLayout {
- private ActivityStarter mActivityStarter;
@NonNull
private ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
@@ -59,10 +57,6 @@ public class BrightnessSliderView extends FrameLayout {
super(context, attrs);
}
- public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
- mActivityStarter = activityStarter;
- }
-
// Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
@@ -71,7 +65,6 @@ public class BrightnessSliderView extends FrameLayout {
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
- mSlider.setActivityStarter(mActivityStarter);
// Finds the progress drawable. Assumes brightness_progress_drawable.xml
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 6ec10da28000..a5a0ae70045e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -23,9 +23,8 @@ import android.view.MotionEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
-import androidx.annotation.NonNull;
-
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
public class ToggleSeekBar extends SeekBar {
@@ -33,8 +32,6 @@ public class ToggleSeekBar extends SeekBar {
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin = null;
- private ActivityStarter mActivityStarter;
-
public ToggleSeekBar(Context context) {
super(context);
}
@@ -52,7 +49,7 @@ public class ToggleSeekBar extends SeekBar {
if (mEnforcedAdmin != null) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
return true;
}
if (!isEnabled()) {
@@ -77,8 +74,4 @@ public class ToggleSeekBar extends SeekBar {
public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
}
-
- public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
- mActivityStarter = activityStarter;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 823caa0805bd..c810786681f0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -29,7 +29,6 @@ import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -41,7 +40,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q
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.android.systemui.statusbar.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.util.DumpUtilsKt.asIndenting;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -64,7 +62,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.Process;
import android.os.Trace;
import android.os.UserManager;
import android.os.VibrationEffect;
@@ -137,6 +134,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInterac
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
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.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;
@@ -185,6 +184,8 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -357,6 +358,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final NotificationGutsManager mGutsManager;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final QuickSettingsController mQsController;
+ private final NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
private final TouchHandler mTouchHandler = new TouchHandler();
private long mDownTime;
@@ -409,6 +411,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private float mOverStretchAmount;
private float mDownX;
private float mDownY;
+ private boolean mIsTrackpadReverseScroll;
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
private int mDisplayLeftInset = 0; // in pixels
@@ -603,6 +606,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
+ private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
@@ -773,11 +777,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
+ ActiveNotificationsInteractor activeNotificationsInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
SplitShadeStateController splitShadeStateController,
PowerInteractor powerInteractor,
- KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm) {
+ KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
+ NaturalScrollingSettingObserver naturalScrollingSettingObserver) {
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -802,10 +808,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
+ mActiveNotificationsInteractor = activeNotificationsInteractor;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
mClockPositionAlgorithm = keyguardClockPositionAlgorithm;
+ mNaturalScrollingSettingObserver = naturalScrollingSettingObserver;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1265,7 +1273,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.onDestroy();
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
// 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
@@ -1318,7 +1326,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 (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mNotificationStackScrollLayoutController.setOverExpansion(0);
mNotificationStackScrollLayoutController.setOverScrollAmount(0);
}
@@ -1339,7 +1347,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
updateClockAppearance();
mQsController.updateQsState();
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mNotificationStackScrollLayoutController.updateFooter();
}
}
@@ -1371,7 +1379,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
void reInflateViews() {
debugLog("reInflateViews");
// Re-inflate the status view group.
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
KeyguardStatusView keyguardStatusView =
mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView);
@@ -1483,16 +1491,16 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void updateMaxDisplayedNotifications(boolean recompute) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
+ return;
+ }
+
if (recompute) {
setMaxDisplayedNotifications(Math.max(computeMaxKeyguardNotifications(), 1));
} else {
if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return;
- }
-
if (isKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
@@ -1640,7 +1648,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.setLockscreenClockY(
mClockPositionAlgorithm.getExpandedPreferredClockY());
}
@@ -1654,7 +1662,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
@@ -1727,7 +1735,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
ConstraintLayout layout;
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
layout = mKeyguardViewConfigurator.getKeyguardRootView();
} else {
layout = mNotificationContainerParent;
@@ -1792,9 +1800,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private boolean hasVisibleNotifications() {
- return mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
+ if (FooterViewRefactor.isEnabled()) {
+ return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
+ } else {
+ return mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
+ }
}
/** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
@@ -1902,7 +1915,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
// TODO (b/296373478) This is for split shade media movement.
} else {
mKeyguardStatusViewController
@@ -2482,7 +2495,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
void requestScrollerTopPaddingUpdate(boolean animate) {
float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
getKeyguardNotificationStaticPadding(), mExpandedFraction);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
mSharedNotificationContainerInteractor.setTopPosition(padding);
} else {
mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
@@ -2673,7 +2686,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
&& !mQsController.getFullyExpanded()) {
alpha *= mClockPositionResult.clockAlpha;
}
- mNotificationStackScrollLayoutController.setAlpha(alpha);
+ mNotificationStackScrollLayoutController.setMaxAlphaForExpansion(alpha);
}
private float getFadeoutAlpha() {
@@ -2840,16 +2853,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
if (!mStatusBarStateController.isDozing()) {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(mView, HapticFeedbackConstants.REJECT);
- } else {
- mVibratorHelper.vibrate(
- Process.myUid(),
- mView.getContext().getPackageName(),
- ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT,
- "falsing-additional-tap-required",
- TOUCH_VIBRATION_ATTRIBUTES);
- }
+ mVibratorHelper.performHapticFeedback(mView, HapticFeedbackConstants.REJECT);
}
}
@@ -2949,7 +2953,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void onScreenTurningOn() {
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.dozeTimeTick();
}
}
@@ -3010,7 +3014,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void setBouncerShowing(boolean bouncerShowing) {
mBouncerShowing = bouncerShowing;
- mNotificationStackScrollLayoutController.updateShowEmptyShadeView();
+ if (!FooterViewRefactor.isEnabled()) {
+ mNotificationStackScrollLayoutController.updateShowEmptyShadeView();
+ }
updateVisibility();
}
@@ -3201,7 +3207,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
public void dozeTimeTick() {
mLockIconViewController.dozeTimeTick();
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.dozeTimeTick();
}
if (mInterpolatedDarkAmount > 0) {
@@ -3677,14 +3683,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void maybeVibrateOnOpening(boolean openingWithTouch) {
if (mVibrateOnOpening && mBarState != KEYGUARD && mBarState != SHADE_LOCKED) {
if (!openingWithTouch || !mHasVibratedOnOpen) {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_START
- );
- } else {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
- }
+ mVibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_START
+ );
mHasVibratedOnOpen = true;
mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true");
}
@@ -3697,7 +3699,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
private boolean isDirectionUpwards(float x, float y) {
float xDiff = x - mInitialExpandX;
- float yDiff = y - mInitialExpandY;
+ float yDiff = (mIsTrackpadReverseScroll ? -1 : 1) * (y - mInitialExpandY);
if (yDiff >= 0) {
return false;
}
@@ -3734,7 +3736,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
|| (!isFullyExpanded() && !isFullyCollapsed())
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
mVelocityTracker.computeCurrentVelocity(1000);
- float vel = mVelocityTracker.getYVelocity();
+ float vel = (mIsTrackpadReverseScroll ? -1 : 1) * mVelocityTracker.getYVelocity();
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
@@ -3773,8 +3775,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
}
+ float dy = (mIsTrackpadReverseScroll ? -1 : 1) * (y - mInitialExpandY);
@Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
- : y - mInitialExpandY > 0 ? QUICK_SETTINGS
+ : dy > 0 ? QUICK_SETTINGS
: (mKeyguardStateController.canDismissLockScreen()
? UNLOCK : BOUNCER_UNLOCK);
@@ -3801,7 +3804,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private float getCurrentExpandVelocity() {
mVelocityTracker.computeCurrentVelocity(1000);
- return mVelocityTracker.getYVelocity();
+ return (mIsTrackpadReverseScroll ? -1 : 1) * mVelocityTracker.getYVelocity();
}
private void endClosing() {
@@ -4427,7 +4430,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 (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX,
mClockPositionResult.clockYFullyDozing,
@@ -4547,7 +4550,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
setDozing(true /* dozing */, false /* animate */);
mStatusBarStateController.setUpcomingState(KEYGUARD);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
mStatusBarStateController.setState(KEYGUARD);
} else {
mStatusBarStateListener.onStateChanged(KEYGUARD);
@@ -4608,7 +4611,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot (used by anti-burnin transformations)
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
}
@@ -4697,7 +4700,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
NotificationStackScrollLayoutController stackScroller) {
return (Float alpha) -> {
mKeyguardStatusViewController.setAlpha(alpha);
- stackScroller.setAlpha(alpha);
+ stackScroller.setMaxAlphaForExpansion(alpha);
if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setAlpha(alpha);
@@ -4718,7 +4721,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private Consumer<Float> setTransitionY(
NotificationStackScrollLayoutController stackScroller) {
return (Float translationY) -> {
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mKeyguardStatusViewController.setTranslationY(translationY,
/* excludeMedia= */false);
stackScroller.setTranslationY(translationY);
@@ -4760,7 +4763,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) {
return false;
}
@@ -4842,6 +4845,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
+ " mAnimatingOnDown: true, mClosing: true");
return true;
}
+
+ mIsTrackpadReverseScroll =
+ !mNaturalScrollingSettingObserver.isNaturalScrollingEnabled()
+ && isTrackpadScroll(mTrackpadGestureFeaturesEnabled, event);
if (!isTracking() || isFullyCollapsed()) {
mInitialExpandY = y;
mInitialExpandX = x;
@@ -4884,7 +4891,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
break;
case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialExpandY;
+ final float h = (mIsTrackpadReverseScroll ? -1 : 1) * (y - mInitialExpandY);
addMovement(event);
final boolean openShadeWithoutHun =
mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown;
@@ -4926,7 +4933,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) {
return false;
}
@@ -5148,7 +5155,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (!isFullyCollapsed()) {
maybeVibrateOnOpening(true /* openingWithTouch */);
}
- float h = y - mInitialExpandY;
+ float h = (mIsTrackpadReverseScroll ? -1 : 1) * (y - mInitialExpandY);
// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
@@ -5197,6 +5204,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mQsController.cancelJankMonitoring();
}
}
+ mIsTrackpadReverseScroll = false;
break;
}
return !mGestureWaitForTouchSlop || isTracking();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index a2ca49d9ba57..cf1dfdc3f701 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -46,6 +46,7 @@ import com.android.systemui.communal.data.repository.CommunalRepository;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
@@ -53,6 +54,7 @@ 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.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
@@ -447,7 +449,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
boolean bouncerShowing;
- if (mFeatureFlagsClassic.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
|| mAlternateBouncerInteractor.isVisibleState();
} else {
@@ -457,7 +459,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
- if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
// When on lockscreen, if the touch originates at the top of the screen
// go directly to QS and not the shade
if (mQuickSettingsController.shouldQuickSettingsIntercept(
@@ -469,7 +471,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
if (result) {
mLastInterceptWasDragDownHelper = true;
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -501,7 +503,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- if (!mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
}
cancellation.recycle();
@@ -516,7 +518,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
- if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
// we still want to finish our drag down gesture when locking the screen
handled |= mDragDownHelper.onTouchEvent(ev) || handled;
@@ -602,7 +604,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
- if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
// 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 866cfb443b0a..9c8a286b2918 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
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
@@ -129,7 +130,6 @@ class NotificationsQSContainerController @Inject constructor(
isGestureNavigation = QuickStepContract.isGesturalMode(currentMode)
mView.setStackScroller(notificationStackScrollLayoutController.getView())
- mView.setMigratingNSSL(featureFlags.isEnabled(Flags.MIGRATE_NSSL))
if (featureFlags.isEnabled(Flags.QS_CONTAINER_GRAPH_OPTIMIZER)){
mView.enableGraphOptimization()
}
@@ -283,7 +283,7 @@ class NotificationsQSContainerController @Inject constructor(
}
private fun setNotificationsConstraints(constraintSet: ConstraintSet) {
- if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (KeyguardShadeMigrationNssl.isEnabled) {
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 af44c4e36315..de3d16a57a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -32,6 +32,7 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.res.R;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
@@ -59,7 +60,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private QS mQs;
private View mQSContainer;
private int mLastQSPaddingBottom;
- private boolean mIsMigratingNSSL;
/**
* These are used to compute the bounding box containing the shade and the notification scrim,
@@ -180,10 +180,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
super.dispatchDraw(canvas);
}
- void setMigratingNSSL(boolean isMigrating) {
- mIsMigratingNSSL = isMigrating;
- }
-
void enableGraphOptimization() {
setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
}
@@ -196,7 +192,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (mIsMigratingNSSL) {
+ if (KeyguardShadeMigrationNssl.isEnabled()) {
return super.drawChild(canvas, child, drawingTime);
}
int layoutIndex = mLayoutDrawingOrder.indexOf(child);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
index c8b6a2e9761b..bbcf10be5ff0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -6,8 +6,12 @@ per-file *Notification* = file:../statusbar/notification/OWNERS
per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com
per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com
-per-file *ShadeHeader* = kozynski@google.com, asc@google.com
-per-file *Shade* = justinweir@google.com
+per-file *ShadeHeader* = syeonlee@google.com, kozynski@google.com, asc@google.com
+
+per-file *Interactor* = set noparent
+per-file *Interactor* = justinweir@google.com, syeonlee@google.com, nijamkin@google.com
+per-file *Repository* = set noparent
+per-file *Repository* = justinweir@google.com, syeonlee@google.com, nijamkin@google.com
per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com
per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index d73fa1460bd4..e84bfc512eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -68,10 +68,9 @@ import com.android.systemui.Dumpable;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+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.plugins.FalsingManager;
@@ -88,6 +87,8 @@ import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -155,10 +156,10 @@ public class QuickSettingsController implements Dumpable {
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
private final CastController mCastController;
private final SplitShadeStateController mSplitShadeStateController;
- private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final ShadeRepository mShadeRepository;
private final ShadeInteractor mShadeInteractor;
+ private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
private final JavaAdapter mJavaAdapter;
private final FalsingManager mFalsingManager;
private final AccessibilityManager mAccessibilityManager;
@@ -209,12 +210,6 @@ public class QuickSettingsController implements Dumpable {
/** Indicates QS is at its max height */
private boolean mFullyExpanded;
- /**
- * Determines if QS should be already expanded when expanding shade.
- * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
- * It needs to be set when movement starts as it resets at the end of expansion/collapse.
- */
- private boolean mExpandImmediate;
private boolean mExpandedWhenExpandingStarted;
private boolean mAnimatingHiddenFromCollapsed;
private boolean mVisible;
@@ -255,6 +250,14 @@ public class QuickSettingsController implements Dumpable {
private Insets mCachedGestureInsets;
/**
+ * The window width currently in effect -- used together with
+ * {@link QuickSettingsController#mCachedGestureInsets} to decide whether a back gesture should
+ * receive a horizontal swipe inwards from the left/right vertical edge of the screen.
+ * We cache this on ACTION_DOWN, and query it during both ACTION_DOWN and ACTION_MOVE events.
+ */
+ private int mCachedWindowWidth;
+
+ /**
* The amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
* shade. This value can also go beyond 1.1 when we're overshooting!
@@ -333,13 +336,13 @@ public class QuickSettingsController implements Dumpable {
AccessibilityManager accessibilityManager,
LockscreenGestureLogger lockscreenGestureLogger,
MetricsLogger metricsLogger,
- FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
ShadeLogger shadeLog,
DumpManager dumpManager,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
ShadeRepository shadeRepository,
ShadeInteractor shadeInteractor,
+ ActiveNotificationsInteractor activeNotificationsInteractor,
JavaAdapter javaAdapter,
CastController castController,
SplitShadeStateController splitShadeStateController
@@ -384,10 +387,10 @@ public class QuickSettingsController implements Dumpable {
mShadeLog = shadeLog;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
mCastController = castController;
- mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
mShadeRepository = shadeRepository;
mShadeInteractor = shadeInteractor;
+ mActiveNotificationsInteractor = activeNotificationsInteractor;
mJavaAdapter = javaAdapter;
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
@@ -516,7 +519,7 @@ public class QuickSettingsController implements Dumpable {
/** */
@VisibleForTesting
boolean isExpandImmediate() {
- return mExpandImmediate;
+ return mShadeRepository.getLegacyExpandImmediate().getValue();
}
float getInitialTouchY() {
@@ -538,6 +541,7 @@ public class QuickSettingsController implements Dumpable {
WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets(
WindowInsets.Type.systemGestures());
+ mCachedWindowWidth = windowMetrics.getBounds().width();
}
/**
@@ -546,7 +550,7 @@ public class QuickSettingsController implements Dumpable {
*/
public boolean shouldBackBypassQuickSettings(float touchX) {
return (touchX < mCachedGestureInsets.left)
- || (touchX > mKeyguardStatusBar.getWidth() - mCachedGestureInsets.right);
+ || (touchX > mCachedWindowWidth - mCachedGestureInsets.right);
}
/** Returns whether touch is within QS area */
@@ -606,7 +610,7 @@ public class QuickSettingsController implements Dumpable {
// close the whole shade with one motion. Also this will be always true when closing
// split shade as there QS are always expanded so every collapsing motion is motion from
// expanded QS to closed panel
- return mExpandImmediate || (getExpanded()
+ return isExpandImmediate() || (getExpanded()
&& !isTracking() && !isExpansionAnimating()
&& !mExpansionFromOverscroll);
}
@@ -724,7 +728,9 @@ public class QuickSettingsController implements Dumpable {
/** Closes the Qs customizer. */
public void closeQsCustomizer() {
- mQs.closeCustomizer();
+ if (mQs != null) {
+ mQs.closeCustomizer();
+ }
}
/** Returns whether touches from the notification panel should be disallowed */
@@ -794,7 +800,7 @@ public class QuickSettingsController implements Dumpable {
&& mBarState == SHADE) {
Log.wtf(TAG,
"setting QS height to 0 in split shade while shade is open(ing). "
- + "Value of mExpandImmediate = " + mExpandImmediate);
+ + "Value of isExpandImmediate() = " + isExpandImmediate());
}
int maxHeight = getMaxExpansionHeight();
height = Math.min(Math.max(
@@ -943,10 +949,9 @@ public class QuickSettingsController implements Dumpable {
}
void setExpandImmediate(boolean expandImmediate) {
- if (expandImmediate != mExpandImmediate) {
+ if (expandImmediate != isExpandImmediate()) {
mShadeLog.logQsExpandImmediateChanged(expandImmediate);
- mExpandImmediate = expandImmediate;
- mShadeExpansionStateManager.notifyExpandImmediateChange(expandImmediate);
+ mShadeRepository.setLegacyExpandImmediate(expandImmediate);
}
}
@@ -982,7 +987,10 @@ public class QuickSettingsController implements Dumpable {
void updateQsState() {
boolean qsFullScreen = getExpanded() && !mSplitShadeEnabled;
- mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
+ mShadeRepository.setLegacyQsFullscreen(qsFullScreen);
+ if (!FooterViewRefactor.isEnabled()) {
+ mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
+ }
mNotificationStackScrollLayoutController.setScrollingEnabled(
mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll));
@@ -998,7 +1006,7 @@ public class QuickSettingsController implements Dumpable {
public void updateExpansion() {
if (mQs == null) return;
final float squishiness;
- if ((mExpandImmediate || getExpanded()) && !mSplitShadeEnabled) {
+ if ((isExpandImmediate() || getExpanded()) && !mSplitShadeEnabled) {
squishiness = 1;
} else if (mTransitioningToFullShadeProgress > 0.0f) {
squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction();
@@ -1776,7 +1784,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 (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
}
}
@@ -1821,7 +1829,7 @@ public class QuickSettingsController implements Dumpable {
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
}
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
@@ -2079,8 +2087,8 @@ public class QuickSettingsController implements Dumpable {
ipw.println(getExpanded());
ipw.print("mFullyExpanded=");
ipw.println(mFullyExpanded);
- ipw.print("mExpandImmediate=");
- ipw.println(mExpandImmediate);
+ ipw.print("isExpandImmediate()=");
+ ipw.println(isExpandImmediate());
ipw.print("mExpandedWhenExpandingStarted=");
ipw.println(mExpandedWhenExpandingStarted);
ipw.print("mAnimatingHiddenFromCollapsed=");
@@ -2113,6 +2121,8 @@ public class QuickSettingsController implements Dumpable {
ipw.println(mAnimatorExpand);
ipw.print("mCachedGestureInsets=");
ipw.println(mCachedGestureInsets);
+ ipw.print("mCachedWindowWidth=");
+ ipw.println(mCachedWindowWidth);
ipw.print("mTransitioningToFullShadeProgress=");
ipw.println(mTransitioningToFullShadeProgress);
ipw.print("mDistanceForFullShadeTransition=");
@@ -2227,8 +2237,12 @@ public class QuickSettingsController implements Dumpable {
mLockscreenShadeTransitionController.getQSDragProgress());
setExpansionHeight(qsHeight);
}
- if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
- && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
+
+ boolean hasNotifications = FooterViewRefactor.isEnabled()
+ ? mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
+ : mNotificationStackScrollLayoutController.getVisibleNotificationCount()
+ != 0;
+ if (!hasNotifications && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
// No notifications are visible, let's animate to the height of qs instead
if (isQsFragmentCreated()) {
// Let's interpolate to the header height instead of the top padding,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index 7a803867d4a4..53eccfdf70d5 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.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractorEmptyImpl
import dagger.Binds
import dagger.Module
@@ -30,4 +32,8 @@ abstract class ShadeEmptyImplModule {
@Binds
@SysUISingleton
abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeInteractor(si: ShadeInteractorEmptyImpl): ShadeInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index fca98f580702..e20534cc7840 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -169,12 +169,6 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
}
}
- fun notifyExpandImmediateChange(expandImmediateEnabled: Boolean) {
- for (cb in shadeStateEventsListeners) {
- cb.onExpandImmediateChanged(expandImmediateEnabled)
- }
- }
-
private fun debugLog(msg: String) {
if (!DEBUG) return
Log.v(TAG, msg)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 89aaaafbcdf3..54467cffa401 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -17,13 +17,40 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
-
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
+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 dagger.Binds
import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
/** Module for classes related to the notification shade. */
@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
abstract class ShadeModule {
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideBaseShadeInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
+ sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+ ): BaseShadeInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
+ }
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeInteractor(si: ShadeInteractorImpl): ShadeInteractor
+
@Binds
@SysUISingleton
abstract fun bindsShadeViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 5804040a8676..c8511d76f5f0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -35,16 +35,5 @@ interface ShadeStateEvents {
* Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/
fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
-
- /**
- * Invoked when the "expand immediate" attribute changes.
- *
- * An example of expanding immediately is when swiping down from the top with two fingers.
- * Instead of going to QQS, we immediately expand to full QS.
- *
- * Another example is when full QS is showing, and we swipe up from the bottom. Instead of
- * going to QQS, the panel fully collapses.
- */
- fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 37073a6b5a50..f40be4bbb678 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -22,6 +22,7 @@ import android.os.Handler
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleView
@@ -68,6 +69,7 @@ abstract class ShadeViewProviderModule {
sceneContainerFlags: SceneContainerFlags,
viewModelProvider: Provider<SceneContainerViewModel>,
containerConfigProvider: Provider<SceneContainerConfig>,
+ flagsProvider: Provider<SceneContainerFlags>,
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
layoutInsetController: NotificationInsetsController,
): WindowRootView {
@@ -77,6 +79,9 @@ abstract class ShadeViewProviderModule {
sceneWindowRootView.init(
viewModel = viewModelProvider.get(),
containerConfig = containerConfigProvider.get(),
+ sharedNotificationContainer =
+ sceneWindowRootView.requireViewById(R.id.shared_notification_container),
+ flags = flagsProvider.get(),
scenes = scenesProvider.get(),
layoutInsetController = layoutInsetController,
)
@@ -140,8 +145,14 @@ abstract class ShadeViewProviderModule {
@SysUISingleton
fun providesLightRevealScrim(
notificationShadeWindowView: NotificationShadeWindowView,
+ scrimLogger: ScrimLogger,
): LightRevealScrim {
- return notificationShadeWindowView.requireViewById(R.id.light_reveal_scrim)
+ val scrim =
+ notificationShadeWindowView.requireViewById<LightRevealScrim>(
+ R.id.light_reveal_scrim
+ )
+ scrim.scrimLogger = scrimLogger
+ return scrim
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index e2e4556f59f7..47b08fe037a4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -65,6 +65,10 @@ interface ShadeRepository {
*/
@Deprecated("Use ShadeInteractor instead") val legacyShadeTracking: StateFlow<Boolean>
+ /** Specifically tracks the user expanding the shade on the lockscreen only */
+ @Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
+ val legacyLockscreenShadeTracking: MutableStateFlow<Boolean>
+
/**
* QuickSettingsController.mTracking as a flow. "Tracking" means that the user is moving quick
* settings up or down with a pointer. Going forward, this concept will be replaced by checks
@@ -89,6 +93,29 @@ interface ShadeRepository {
*/
@Deprecated("Use ShadeInteractor instead") val legacyIsQsExpanded: StateFlow<Boolean>
+ /**
+ * QuickSettingsController.mExpandImmediate as a flow. Indicates that Quick Settings is being
+ * expanded without first expanding the Shade or Quick Settings is being collapsed without first
+ * collapsing to shade, i.e. expanding with 2-finger swipe or collapsing by flinging from the
+ * bottom of the screen. Replaced by ShadeInteractor.isQsBypassingShade.
+ */
+ @Deprecated("Use ShadeInteractor.isQsBypassingShade instead")
+ val legacyExpandImmediate: StateFlow<Boolean>
+
+ /** True when QS is taking up the entire screen, i.e. fully expanded on a non-unfolded phone. */
+ @Deprecated("Use ShadeInteractor instead") val legacyQsFullscreen: StateFlow<Boolean>
+
+ /** */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyQsFullscreen(legacyQsFullscreen: Boolean)
+
+ /**
+ * Sets whether Quick Settings is being expanded without first expanding the Shade or Quick
+ * Settings is being collapsed without first collapsing to shade.
+ */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyExpandImmediate(legacyExpandImmediate: Boolean)
+
/** Sets whether QS is expanded. */
@Deprecated("Use ShadeInteractor instead")
fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean)
@@ -101,10 +128,14 @@ interface ShadeRepository {
fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
/** Sets whether the user is moving Quick Settings with a pointer */
- fun setLegacyQsTracking(legacyQsTracking: Boolean)
+ @Deprecated("Use ShadeInteractor instead") fun setLegacyQsTracking(legacyQsTracking: Boolean)
/** Sets whether the user is moving the shade with a pointer */
- fun setLegacyShadeTracking(tracking: Boolean)
+ @Deprecated("Use ShadeInteractor instead") fun setLegacyShadeTracking(tracking: Boolean)
+
+ /** Sets whether the user is moving the shade with a pointer, on lockscreen only */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyLockscreenShadeTracking(tracking: Boolean)
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
@@ -177,6 +208,8 @@ constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepos
@Deprecated("Use ShadeInteractor instead")
override val legacyShadeTracking: StateFlow<Boolean> = _legacyShadeTracking.asStateFlow()
+ override val legacyLockscreenShadeTracking = MutableStateFlow(false)
+
private val _legacyQsTracking = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead")
override val legacyQsTracking: StateFlow<Boolean> = _legacyQsTracking.asStateFlow()
@@ -190,6 +223,22 @@ constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepos
@Deprecated("Use ShadeInteractor instead")
override val legacyIsQsExpanded: StateFlow<Boolean> = _legacyIsQsExpanded.asStateFlow()
+ private val _legacyExpandImmediate = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandImmediate: StateFlow<Boolean> = _legacyExpandImmediate.asStateFlow()
+
+ private val _legacyQsFullscreen = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyQsFullscreen: StateFlow<Boolean> = _legacyQsFullscreen.asStateFlow()
+
+ override fun setLegacyQsFullscreen(legacyQsFullscreen: Boolean) {
+ _legacyQsFullscreen.value = legacyQsFullscreen
+ }
+
+ override fun setLegacyExpandImmediate(legacyExpandImmediate: Boolean) {
+ _legacyExpandImmediate.value = legacyExpandImmediate
+ }
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
_legacyIsQsExpanded.value = legacyIsQsExpanded
@@ -212,6 +261,11 @@ constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepos
_legacyShadeTracking.value = tracking
}
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyLockscreenShadeTracking(tracking: Boolean) {
+ legacyLockscreenShadeTracking.value = tracking
+ }
+
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index b2ffeb3f2925..6a9757f3adfd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -16,149 +16,42 @@
package com.android.systemui.shade.domain.interactor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
-import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
-import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
-import javax.inject.Inject
-import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.currentCoroutineContext
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.first
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.isActive
/** Business logic for shade interactions. */
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-class ShadeInteractor
-@Inject
-constructor(
- @Application scope: CoroutineScope,
- deviceProvisioningRepository: DeviceProvisioningRepository,
- disableFlagsRepository: DisableFlagsRepository,
- dozeParams: DozeParameters,
- sceneContainerFlags: SceneContainerFlags,
- // TODO(b/300258424) convert to direct reference instead of provider
- sceneInteractorProvider: Provider<SceneInteractor>,
- keyguardRepository: KeyguardRepository,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
- powerInteractor: PowerInteractor,
- userSetupRepository: UserSetupRepository,
- userSwitcherInteractor: UserSwitcherInteractor,
- sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
- repository: ShadeRepository,
-) {
+interface ShadeInteractor : BaseShadeInteractor {
/** Emits true if the shade is currently allowed and false otherwise. */
- val isShadeEnabled: StateFlow<Boolean> =
- disableFlagsRepository.disableFlags
- .map { it.isShadeEnabled() }
- .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ val isShadeEnabled: StateFlow<Boolean>
- /**
- * Whether split shade, the combined notifications and quick settings shade used for large
- * screens, is enabled.
- */
- val isSplitShadeEnabled: Flow<Boolean> =
- sharedNotificationContainerInteractor.configurationBasedDimensions
- .map { dimens -> dimens.useSplitShade }
- .distinctUntilChanged()
+ /** Whether either the shade or QS is fully expanded. */
+ val isAnyFullyExpanded: Flow<Boolean>
- /** The amount [0-1] that the shade has been opened */
- val shadeExpansion: Flow<Float> =
- if (sceneContainerFlags.isEnabled()) {
- sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.Shade)
- } else {
- combine(
- repository.lockscreenShadeExpansion,
- keyguardRepository.statusBarState,
- repository.legacyShadeExpansion,
- repository.qsExpansion,
- isSplitShadeEnabled
- ) {
- lockscreenShadeExpansion,
- statusBarState,
- legacyShadeExpansion,
- qsExpansion,
- splitShadeEnabled ->
- when (statusBarState) {
- // legacyShadeExpansion is 1 instead of 0 when QS is expanded
- StatusBarState.SHADE ->
- if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
- StatusBarState.KEYGUARD -> lockscreenShadeExpansion
- // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
- // the pointer is lifted and the lockscreen shade is fully expanded
- StatusBarState.SHADE_LOCKED -> 1f
- }
- }
- .distinctUntilChanged()
- }
+ /** Whether the Shade is fully expanded. */
+ val isShadeFullyExpanded: Flow<Boolean>
/**
- * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
- * report 0f. If split shade is enabled, value matches shadeExpansion.
+ * Whether the user is expanding or collapsing either the shade or quick settings with user
+ * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
+ * but a transition they initiated is still animating.
*/
- val qsExpansion: StateFlow<Float> =
- if (sceneContainerFlags.isEnabled()) {
- val qsExp = sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
- combine(isSplitShadeEnabled, shadeExpansion, qsExp) {
- isSplitShadeEnabled,
- shadeExp,
- qsExp ->
- if (isSplitShadeEnabled) {
- shadeExp
- } else {
- qsExp
- }
- }
- .stateIn(scope, SharingStarted.Eagerly, 0f)
- } else {
- repository.qsExpansion
- }
+ val isUserInteracting: Flow<Boolean>
- /** Whether Quick Settings is expanded a non-zero amount. */
- val isQsExpanded: StateFlow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- qsExpansion
- .map { it > 0 }
- .distinctUntilChanged()
- .stateIn(scope, SharingStarted.Eagerly, false)
- } else {
- repository.legacyIsQsExpanded
- }
+ /** Are touches allowed on the notification panel? */
+ val isShadeTouchable: Flow<Boolean>
- /** The amount [0-1] either QS or the shade has been opened. */
- val anyExpansion: StateFlow<Float> =
- combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
- .stateIn(scope, SharingStarted.Eagerly, 0f)
+ /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
+ val isExpandToQsEnabled: Flow<Boolean>
+}
- /** Whether either the shade or QS is fully expanded. */
- val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
+/** ShadeInteractor methods with implementations that differ between non-empty impls. */
+interface BaseShadeInteractor {
+ /** The amount [0-1] either QS or the shade has been opened. */
+ val anyExpansion: StateFlow<Float>
/**
* Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
@@ -169,141 +62,53 @@ constructor(
*
* TODO(b/300258424) remove all but the first sentence of this comment
*/
- val isAnyExpanded: StateFlow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- anyExpansion.map { it > 0f }.distinctUntilChanged()
- } else {
- repository.legacyExpandedOrAwaitingInputTransfer
- }
- .stateIn(scope, SharingStarted.Eagerly, false)
+ val isAnyExpanded: StateFlow<Boolean>
+
+ /** The amount [0-1] that the shade has been opened. */
+ val shadeExpansion: Flow<Float>
/**
- * Whether the user is expanding or collapsing the shade with user input. This will be true even
- * if the user's input gesture has ended but a transition they initiated is animating.
+ * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
+ * report 0f. If split shade is enabled, value matches shadeExpansion.
*/
- val isUserInteractingWithShade: Flow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
- } else {
- userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion)
- }
+ val qsExpansion: StateFlow<Float>
+
+ /** Whether Quick Settings is expanded a non-zero amount. */
+ val isQsExpanded: StateFlow<Boolean>
/**
- * Whether the user is expanding or collapsing quick settings with user input. This will be true
- * even if the user's input gesture has ended but a transition they initiated is still
- * animating.
+ * Emits true whenever Quick Settings is being expanded without first expanding the Shade or if
+ * if Quick Settings is being collapsed without first collapsing to shade, i.e. expanding with
+ * 2-finger swipe or collapsing by flinging from the bottom of the screen. This concept was
+ * previously called "expand immediate" in the legacy codebase.
*/
- val isUserInteractingWithQs: Flow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings)
- } else {
- userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
- }
+ val isQsBypassingShade: Flow<Boolean>
/**
- * Whether the user is expanding or collapsing either the shade or quick settings with user
- * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
- * but a transition they initiated is still animating.
+ * Emits true when QS is displayed over the entire screen of the device. Currently, this only
+ * happens on phones that are not unfolded when QS expansion is equal to 1.
*/
- val isUserInteracting: Flow<Boolean> =
- combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
- .distinctUntilChanged()
-
- /** Are touches allowed on the notification panel? */
- val isShadeTouchable: Flow<Boolean> =
- combine(
- powerInteractor.isAsleep,
- keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
- keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
- deviceProvisioningRepository.isFactoryResetProtectionActive,
- ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
- when {
- // Touches are disabled when Factory Reset Protection is active
- isFrpActive -> false
- // If the device is going to sleep, only accept touches if we're still
- // animating
- goingToSleep -> dozeParams.shouldControlScreenOff()
- // If the device is asleep, only accept touches if there's a pulse
- isAsleep -> isPulsing
- else -> true
- }
- }
-
- /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
- val isExpandToQsEnabled: Flow<Boolean> =
- combine(
- disableFlagsRepository.disableFlags,
- isShadeEnabled,
- keyguardRepository.isDozing,
- userSetupRepository.isUserSetupFlow,
- deviceProvisioningRepository.isDeviceProvisioned,
- ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
- isDeviceProvisioned &&
- // Disallow QS during setup if it's a simple user switcher. (The user intends to
- // use the lock screen user switcher, QS is not needed.)
- (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
- isShadeEnabled &&
- disableFlags.isQuickSettingsEnabled() &&
- !isDozing
- }
+ val isQsFullscreen: Flow<Boolean>
- fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
- sceneInteractor.transitionState
- .flatMapLatest { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- if (state.scene == sceneKey) {
- flowOf(1f)
- } else {
- flowOf(0f)
- }
- is ObservableTransitionState.Transition ->
- if (state.toScene == sceneKey) {
- state.progress
- } else if (state.fromScene == sceneKey) {
- state.progress.map { progress -> 1 - progress }
- } else {
- flowOf(0f)
- }
- }
- }
- .distinctUntilChanged()
-
- fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
- sceneInteractor.transitionState
- .map { state ->
- when (state) {
- is ObservableTransitionState.Idle -> false
- is ObservableTransitionState.Transition ->
- state.isInitiatedByUserInput &&
- (state.toScene == sceneKey || state.fromScene == sceneKey)
- }
- }
- .distinctUntilChanged()
+ /**
+ * Whether the user is expanding or collapsing the shade with user input. This will be true even
+ * if the user's input gesture has ended but a transition they initiated is animating.
+ */
+ val isUserInteractingWithShade: Flow<Boolean>
/**
- * Return a flow for whether a user is interacting with an expandable shade component using
- * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
- * [expansion.first] checks the current value of the flow.
+ * Whether the user is expanding or collapsing quick settings with user input. This will be true
+ * even if the user's input gesture has ended but a transition they initiated is still
+ * animating.
*/
- private fun userInteractingFlow(
- tracking: Flow<Boolean>,
- expansion: StateFlow<Float>
- ): Flow<Boolean> {
- return flow {
- // initial value is false
- emit(false)
- while (currentCoroutineContext().isActive) {
- // wait for tracking to become true
- tracking.first { it }
- emit(true)
- // wait for tracking to become false
- tracking.first { !it }
- // wait for expansion to complete in either direction
- expansion.first { it <= 0f || it >= 1f }
- // interaction complete
- emit(false)
- }
- }
- }
+ val isUserInteractingWithQs: Flow<Boolean>
+}
+
+fun createAnyExpansionFlow(
+ scope: CoroutineScope,
+ shadeExpansion: Flow<Float>,
+ qsExpansion: Flow<Float>
+): StateFlow<Float> {
+ return combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
+ .stateIn(scope, SharingStarted.Eagerly, 0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
new file mode 100644
index 000000000000..d41c5a66ad82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Empty implementation of ShadeInteractor for System UI variants with no shade. */
+@SysUISingleton
+class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor {
+ private val inactiveFlowBoolean = MutableStateFlow(false)
+ private val inactiveFlowFloat = MutableStateFlow(0f)
+ override val isShadeEnabled: StateFlow<Boolean> = inactiveFlowBoolean
+ override val shadeExpansion: Flow<Float> = inactiveFlowFloat
+ override val qsExpansion: StateFlow<Float> = inactiveFlowFloat
+ override val isQsExpanded: StateFlow<Boolean> = inactiveFlowBoolean
+ override val isQsBypassingShade: Flow<Boolean> = inactiveFlowBoolean
+ override val isQsFullscreen: Flow<Boolean> = inactiveFlowBoolean
+ override val anyExpansion: StateFlow<Float> = inactiveFlowFloat
+ override val isAnyFullyExpanded: Flow<Boolean> = inactiveFlowBoolean
+ override val isShadeFullyExpanded: Flow<Boolean> = inactiveFlowBoolean
+ override val isAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean
+ override val isUserInteractingWithShade: Flow<Boolean> = inactiveFlowBoolean
+ override val isUserInteractingWithQs: Flow<Boolean> = inactiveFlowBoolean
+ override val isUserInteracting: Flow<Boolean> = inactiveFlowBoolean
+ override val isShadeTouchable: Flow<Boolean> = inactiveFlowBoolean
+ override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
new file mode 100644
index 000000000000..68600e9d6918
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
+import javax.inject.Inject
+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.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** The non-empty SceneInteractor implementation. */
+@SysUISingleton
+class ShadeInteractorImpl
+@Inject
+constructor(
+ @Application val scope: CoroutineScope,
+ deviceProvisioningRepository: DeviceProvisioningRepository,
+ disableFlagsRepository: DisableFlagsRepository,
+ dozeParams: DozeParameters,
+ keyguardRepository: KeyguardRepository,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ powerInteractor: PowerInteractor,
+ userSetupRepository: UserSetupRepository,
+ userSwitcherInteractor: UserSwitcherInteractor,
+ private val baseShadeInteractor: BaseShadeInteractor,
+) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
+ override val isShadeEnabled: StateFlow<Boolean> =
+ disableFlagsRepository.disableFlags
+ .map { it.isShadeEnabled() }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+
+ override val isAnyFullyExpanded: Flow<Boolean> =
+ anyExpansion.map { it >= 1f }.distinctUntilChanged()
+
+ override val isShadeFullyExpanded: Flow<Boolean> =
+ baseShadeInteractor.shadeExpansion.map { it >= 1f }.distinctUntilChanged()
+
+ override val isUserInteracting: Flow<Boolean> =
+ combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
+ .distinctUntilChanged()
+
+ override val isShadeTouchable: Flow<Boolean> =
+ combine(
+ powerInteractor.isAsleep,
+ keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
+ keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
+ deviceProvisioningRepository.isFactoryResetProtectionActive,
+ ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
+ when {
+ // Touches are disabled when Factory Reset Protection is active
+ isFrpActive -> false
+ // If the device is going to sleep, only accept touches if we're still
+ // animating
+ goingToSleep -> dozeParams.shouldControlScreenOff()
+ // If the device is asleep, only accept touches if there's a pulse
+ isAsleep -> isPulsing
+ else -> true
+ }
+ }
+
+ override val isExpandToQsEnabled: Flow<Boolean> =
+ combine(
+ disableFlagsRepository.disableFlags,
+ isShadeEnabled,
+ keyguardRepository.isDozing,
+ userSetupRepository.isUserSetupFlow,
+ deviceProvisioningRepository.isDeviceProvisioned,
+ ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
+ isDeviceProvisioned &&
+ // Disallow QS during setup if it's a simple user switcher. (The user intends to
+ // use the lock screen user switcher, QS is not needed.)
+ (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
+ isShadeEnabled &&
+ disableFlags.isQuickSettingsEnabled() &&
+ !isDozing
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
new file mode 100644
index 000000000000..2ac3193d1e8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.currentCoroutineContext
+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.first
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.isActive
+
+/** ShadeInteractor implementation for the legacy codebase, e.g. NPVC. */
+@SysUISingleton
+class ShadeInteractorLegacyImpl
+@Inject
+constructor(
+ @Application val scope: CoroutineScope,
+ keyguardRepository: KeyguardRepository,
+ sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
+ repository: ShadeRepository,
+) : BaseShadeInteractor {
+ /** The amount [0-1] that the shade has been opened */
+ override val shadeExpansion: Flow<Float> =
+ combine(
+ repository.lockscreenShadeExpansion,
+ keyguardRepository.statusBarState,
+ repository.legacyShadeExpansion,
+ repository.qsExpansion,
+ sharedNotificationContainerInteractor.isSplitShadeEnabled
+ ) {
+ lockscreenShadeExpansion,
+ statusBarState,
+ legacyShadeExpansion,
+ qsExpansion,
+ splitShadeEnabled ->
+ when (statusBarState) {
+ // legacyShadeExpansion is 1 instead of 0 when QS is expanded
+ StatusBarState.SHADE ->
+ if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
+ StatusBarState.KEYGUARD -> lockscreenShadeExpansion
+ // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
+ // the pointer is lifted and the lockscreen shade is fully expanded
+ StatusBarState.SHADE_LOCKED -> 1f
+ }
+ }
+ .distinctUntilChanged()
+
+ override val qsExpansion: StateFlow<Float> = repository.qsExpansion
+
+ override val isQsExpanded: StateFlow<Boolean> = repository.legacyIsQsExpanded
+
+ override val isQsBypassingShade: Flow<Boolean> = repository.legacyExpandImmediate
+ override val isQsFullscreen: Flow<Boolean> = repository.legacyQsFullscreen
+
+ override val anyExpansion: StateFlow<Float> =
+ createAnyExpansionFlow(scope, shadeExpansion, qsExpansion)
+
+ override val isAnyExpanded =
+ repository.legacyExpandedOrAwaitingInputTransfer.stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ false
+ )
+
+ override val isUserInteractingWithShade: Flow<Boolean> =
+ combine(
+ userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion),
+ repository.legacyLockscreenShadeTracking
+ ) { legacyShadeTracking, legacyLockscreenShadeTracking ->
+ legacyShadeTracking || legacyLockscreenShadeTracking
+ }
+
+ override val isUserInteractingWithQs: Flow<Boolean> =
+ userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+
+ /**
+ * Return a flow for whether a user is interacting with an expandable shade component using
+ * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
+ * [expansion.first] checks the current value of the flow.
+ */
+ private fun userInteractingFlow(
+ tracking: Flow<Boolean>,
+ expansion: StateFlow<Float>
+ ): Flow<Boolean> {
+ return flow {
+ // initial value is false
+ emit(false)
+ while (currentCoroutineContext().isActive) {
+ // wait for tracking to become true
+ tracking.first { it }
+ emit(true)
+ // wait for tracking to become false
+ tracking.first { !it }
+ // wait for expansion to complete in either direction
+ expansion.first { it <= 0f || it >= 1f }
+ // interaction complete
+ emit(false)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
new file mode 100644
index 000000000000..7cff8ea7abd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.map
+import kotlinx.coroutines.flow.stateIn
+
+/** ShadeInteractor implementation for Scene Container. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class ShadeInteractorSceneContainerImpl
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ sceneInteractor: SceneInteractor,
+ sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
+) : BaseShadeInteractor {
+ override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+
+ private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+
+ override val qsExpansion: StateFlow<Float> =
+ combine(
+ sharedNotificationContainerInteractor.isSplitShadeEnabled,
+ shadeExpansion,
+ sceneBasedQsExpansion,
+ ) { isSplitShadeEnabled, shadeExpansion, qsExpansion ->
+ if (isSplitShadeEnabled) {
+ shadeExpansion
+ } else {
+ qsExpansion
+ }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, 0f)
+
+ override val isQsExpanded: StateFlow<Boolean> =
+ qsExpansion
+ .map { it > 0 }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override val isQsBypassingShade: Flow<Boolean> =
+ sceneInteractor.transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> flowOf(false)
+ is ObservableTransitionState.Transition ->
+ flowOf(
+ state.toScene == SceneKey.QuickSettings &&
+ state.fromScene != SceneKey.Shade
+ )
+ }
+ }
+ .distinctUntilChanged()
+
+ override val isQsFullscreen: Flow<Boolean> =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> state.scene == SceneKey.QuickSettings
+ is ObservableTransitionState.Transition -> false
+ }
+ }
+ .distinctUntilChanged()
+
+ override val anyExpansion: StateFlow<Float> =
+ createAnyExpansionFlow(scope, shadeExpansion, qsExpansion)
+
+ override val isAnyExpanded =
+ anyExpansion
+ .map { it > 0f }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override val isUserInteractingWithShade: Flow<Boolean> =
+ sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+
+ override val isUserInteractingWithQs: Flow<Boolean> =
+ sceneBasedInteracting(sceneInteractor, SceneKey.QuickSettings)
+
+ /**
+ * Returns a flow that uses scene transition progress to and from a scene that is pulled down
+ * from the top of the screen to a 0-1 expansion amount float.
+ */
+ internal fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ if (state.scene == sceneKey) {
+ flowOf(1f)
+ } else {
+ flowOf(0f)
+ }
+ is ObservableTransitionState.Transition ->
+ if (state.toScene == sceneKey) {
+ state.progress
+ } else if (state.fromScene == sceneKey) {
+ state.progress.map { progress -> 1 - progress }
+ } else {
+ flowOf(0f)
+ }
+ }
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Returns a flow that uses scene transition data to determine whether the user is interacting
+ * with a scene that is pulled down from the top of the screen.
+ */
+ internal fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.isInitiatedByUserInput &&
+ (state.toScene == sceneKey || state.fromScene == sceneKey)
+ }
+ }
+ .distinctUntilChanged()
+}
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 9c5a20189dd2..2a071def083a 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
@@ -16,11 +16,12 @@
package com.android.systemui.shade.ui.viewmodel
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
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.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -34,9 +35,10 @@ class ShadeSceneViewModel
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- deviceEntryInteractor: DeviceEntryInteractor,
- private val bouncerInteractor: BouncerInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ val qsSceneAdapter: QSSceneAdapter,
val shadeHeaderViewModel: ShadeHeaderViewModel,
+ val notifications: NotificationsPlaceholderViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: StateFlow<SceneKey> =
@@ -60,9 +62,7 @@ constructor(
)
/** Notifies that some content in the shade was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
+ fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
private fun upDestinationSceneKey(
isUnlocked: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index ab0d6e3a6382..922560f16dce 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -17,11 +17,10 @@
package com.android.systemui.smartspace.config
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
class BcSmartspaceConfigProvider(private val featureFlags: FeatureFlags) :
BcSmartspaceConfigPlugin {
override val isDefaultDateWeatherDisabled: Boolean
- get() = featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)
+ get() = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c2863fb2c330..d88fab0deaa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -75,14 +75,14 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.policy.CallbackController;
+import dagger.Lazy;
+
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import dagger.Lazy;
-
/**
* This class takes the functions from IStatusBar that come in on
* binder pool threads and posts messages to get them onto the main
@@ -424,7 +424,7 @@ public class CommandQueue extends IStatusBar.Stub implements
default void onTracingStateChanged(boolean enabled) { }
/**
- * Requests {@link com.android.systemui.accessibility.WindowMagnification} to invoke
+ * Requests {@link com.android.systemui.accessibility.Magnification} to invoke
* {@code android.view.accessibility.AccessibilityManager#
* setWindowMagnificationConnection(IWindowMagnificationConnection)}
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index de334bbd880c..2338be28d32c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -85,7 +85,9 @@ public class EmptyShadeView extends StackScrollerDecorView {
public void setFooterVisibility(@Visibility int visibility) {
mFooterVisibility = visibility;
- setSecondaryVisible(visibility == View.VISIBLE, false);
+ setSecondaryVisible(/* visible = */ visibility == View.VISIBLE,
+ /* animate = */false,
+ /* onAnimationEnded = */ null);
}
public void setFooterText(@StringRes int text) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 68c48b9a261f..4a5089770b05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -60,6 +60,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -114,7 +115,6 @@ public final class KeyboardShortcutListSearch {
private Button mButtonInput;
private Button mButtonOpenApps;
private Button mButtonSpecificApp;
- private ImageView mEditTextCancel;
private TextView mNoSearchResults;
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
@@ -895,8 +895,9 @@ public final class KeyboardShortcutListSearch {
// Do nothing.
}
});
- mEditTextCancel = keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_search_cancel);
- mEditTextCancel.setOnClickListener(v -> mSearchEditText.setText(null));
+ ImageButton editTextCancel = keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_search_cancel);
+ editTextCancel.setOnClickListener(v -> mSearchEditText.setText(null));
}
private void populateKeyboardShortcutSearchList(LinearLayout keyboardShortcutsLayout) {
@@ -1276,12 +1277,12 @@ public final class KeyboardShortcutListSearch {
private int getColorOfTextColorOnAccent() {
return Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.textColorOnAccent);
+ mContext, com.android.internal.R.attr.materialColorOnPrimary);
}
private int getColorOfTextColorSecondary() {
return Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.textColorSecondary);
+ mContext, com.android.internal.R.attr.materialColorOnSurface);
}
// Create the new data structure for handling the N-to-1 key mapping and other complex case.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 3120128c7967..39b7930ed386 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -18,6 +18,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.animation.PathInterpolator
import com.android.app.animation.Interpolators
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
@@ -89,7 +90,7 @@ object LiftReveal : LightRevealEffect {
}
}
-class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
+data class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
// Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster
private val interpolator =
@@ -155,7 +156,7 @@ class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffe
}
}
-class CircleReveal(
+data class CircleReveal(
/** X-value of the circle center of the reveal. */
val centerX: Int,
/** Y-value of the circle center of the reveal. */
@@ -181,7 +182,7 @@ class CircleReveal(
}
}
-class PowerButtonReveal(
+data class PowerButtonReveal(
/** Approximate Y-value of the center of the power button on the physical device. */
val powerButtonY: Float
) : LightRevealEffect {
@@ -253,7 +254,9 @@ constructor(
) : View(context, attrs) {
/** Listener that is called if the scrim's opaqueness changes */
- lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
+ var isScrimOpaqueChangedListener: Consumer<Boolean>? = null
+
+ var scrimLogger: ScrimLogger? = null
/**
* How much of the underlying views are revealed, in percent. 0 means they will be completely
@@ -263,7 +266,9 @@ constructor(
set(value) {
if (field != value) {
field = value
-
+ if (value <= 0.0f || value >= 1.0f) {
+ scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}")
+ }
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
Trace.traceCounter(
@@ -285,6 +290,7 @@ constructor(
field = value
revealEffect.setRevealAmountOnScrim(revealAmount, this)
+ scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}")
invalidate()
}
}
@@ -301,6 +307,7 @@ constructor(
*/
internal var viewWidth: Int = initialWidth ?: 0
private set
+
internal var viewHeight: Int = initialHeight ?: 0
private set
@@ -342,7 +349,8 @@ constructor(
private set(value) {
if (field != value) {
field = value
- isScrimOpaqueChangedListener.accept(field)
+ isScrimOpaqueChangedListener?.accept(field)
+ scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}")
}
}
@@ -360,11 +368,13 @@ constructor(
override fun setAlpha(alpha: Float) {
super.setAlpha(alpha)
+ scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}")
updateScrimOpaque()
}
override fun setVisibility(visibility: Int) {
super.setVisibility(visibility)
+ scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}")
updateScrimOpaque()
}
@@ -424,11 +434,7 @@ constructor(
}
override fun onDraw(canvas: Canvas) {
- if (
- revealGradientWidth <= 0 ||
- revealGradientHeight <= 0 ||
- revealAmount == 0f
- ) {
+ if (revealGradientWidth <= 0 || revealGradientHeight <= 0 || revealAmount == 0f) {
if (revealAmount < 1f) {
canvas.drawColor(revealGradientEndColor)
}
@@ -461,4 +467,8 @@ constructor(
PorterDuff.Mode.MULTIPLY
)
}
+
+ private fun logString(): String {
+ return this::class.simpleName!! + "@" + hashCode()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index bf722af80934..49c729eada1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -21,7 +21,9 @@ import com.android.systemui.classifier.FalsingCollector
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.media.controls.ui.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
@@ -78,7 +80,8 @@ constructor(
private val shadeRepository: ShadeRepository,
private val shadeInteractor: ShadeInteractor,
private val powerInteractor: PowerInteractor,
- private val splitShadeStateController: SplitShadeStateController
+ private val splitShadeStateController: SplitShadeStateController,
+ private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
) : Dumpable {
private var pulseHeight: Float = 0f
@@ -157,7 +160,15 @@ constructor(
var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null
/** The touch helper responsible for the drag down animation. */
- val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
+ val touchHelper =
+ DragDownHelper(
+ falsingManager,
+ falsingCollector,
+ this,
+ naturalScrollingSettingObserver,
+ shadeRepository,
+ context
+ )
private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
splitShadeOverScrollerFactory.create({ qS }, { nsslController })
@@ -340,6 +351,7 @@ constructor(
)
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
+ shadeRepository.setLegacyLockscreenShadeTracking(false)
setDragDownAmountAnimated(0f)
}
@@ -366,6 +378,7 @@ constructor(
cancel()
}
}
+ shadeRepository.setLegacyLockscreenShadeTracking(true)
}
/** Do we need a falsing check currently? */
@@ -749,6 +762,8 @@ class DragDownHelper(
private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector,
private val dragDownCallback: LockscreenShadeTransitionController,
+ private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+ private val shadeRepository: ShadeRepository,
context: Context
) : Gefingerpoken {
@@ -763,6 +778,7 @@ class DragDownHelper(
private var draggedFarEnough = false
private var startingChild: ExpandableView? = null
private var lastHeight = 0f
+ private var isTrackpadReverseScroll = false
var isDraggingDown = false
private set
@@ -800,9 +816,12 @@ class DragDownHelper(
startingChild = null
initialTouchY = y
initialTouchX = x
+ isTrackpadReverseScroll =
+ !naturalScrollingSettingObserver.isNaturalScrollingEnabled &&
+ isTrackpadScroll(true, event)
}
MotionEvent.ACTION_MOVE -> {
- val h = y - initialTouchY
+ val h = (if (isTrackpadReverseScroll) -1 else 1) * (y - initialTouchY)
// Adjust the touch slop if another gesture may be being performed.
val touchSlop =
if (event.classification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
@@ -832,7 +851,7 @@ class DragDownHelper(
val y = event.y
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> {
- lastHeight = y - initialTouchY
+ lastHeight = (if (isTrackpadReverseScroll) -1 else 1) * (y - initialTouchY)
captureStartingChild(initialTouchX, initialTouchY)
dragDownCallback.dragDownAmount = lastHeight + dragDownAmountOnStart
if (startingChild != null) {
@@ -857,12 +876,15 @@ class DragDownHelper(
!isFalseTouch &&
dragDownCallback.canDragDown()
) {
- dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt())
+ val dragDown = (if (isTrackpadReverseScroll) -1 else 1) * (y - initialTouchY)
+ dragDownCallback.onDraggedDown(startingChild, dragDown.toInt())
if (startingChild != null) {
expandCallback.setUserLockedChild(startingChild, false)
startingChild = null
}
isDraggingDown = false
+ isTrackpadReverseScroll = false
+ shadeRepository.setLegacyLockscreenShadeTracking(false)
} else {
stopDragging()
return false
@@ -941,6 +963,7 @@ class DragDownHelper(
startingChild = null
}
isDraggingDown = false
+ isTrackpadReverseScroll = false
dragDownCallback.onDragDownReset()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 710e59d91c6b..0e83c78edb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -250,6 +250,7 @@ public class NotificationLockscreenUserManagerImpl implements
private final Handler mMainHandler;
private final Handler mBackgroundHandler;
private final Executor mBackgroundExecutor;
+ /** The current user and its profiles (possibly including a communal profile). */
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();
@@ -679,7 +680,7 @@ public class NotificationLockscreenUserManagerImpl implements
}
NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- return entry != null
+ return entry != null && entry.getRanking().getChannel() != null
&& entry.getRanking().getChannel().getLockscreenVisibility()
== Notification.VISIBILITY_PRIVATE;
} else {
@@ -689,12 +690,16 @@ public class NotificationLockscreenUserManagerImpl implements
}
}
+ @SuppressLint("MissingPermission")
private void updateCurrentProfilesCache() {
synchronized (mLock) {
mCurrentProfiles.clear();
mCurrentManagedProfiles.clear();
if (mUserManager != null) {
- for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
+ List<UserInfo> profiles = android.multiuser.Flags.supportCommunalProfile()
+ ? mUserManager.getProfilesIncludingCommunal(mCurrentUserId)
+ : mUserManager.getProfiles(mCurrentUserId);
+ for (UserInfo user : profiles) {
mCurrentProfiles.put(user.id, user);
if (UserManager.USER_TYPE_PROFILE_MANAGED.equals(user.userType)) {
mCurrentManagedProfiles.put(user.id, user);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0b6e4009c4f7..4d373353d7a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -92,6 +92,7 @@ public class NotificationShelf extends ActivatableNotificationView {
private int mIndexOfFirstViewInShelf = -1;
private float mCornerAnimationDistance;
private float mActualWidth = -1;
+ private int mMaxIconsOnLockscreen;
private final RefactorFlag mSensitiveRevealAnim =
RefactorFlag.forView(Flags.SENSITIVE_REVEAL_ANIM);
private boolean mCanModifyColorOfNotifications;
@@ -136,6 +137,7 @@ public class NotificationShelf extends ActivatableNotificationView {
Resources res = getResources();
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
+ mMaxIconsOnLockscreen = res.getInteger(R.integer.max_notif_icons_on_lockscreen);
ViewGroup.LayoutParams layoutParams = getLayoutParams();
final int newShelfHeight = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -227,7 +229,9 @@ public class NotificationShelf extends ActivatableNotificationView {
} else {
viewState.setAlpha(1f - ambientState.getHideAmount());
}
- viewState.belowSpeedBump = getSpeedBumpIndex() == 0;
+ if (!NotificationIconContainerRefactor.isEnabled()) {
+ viewState.belowSpeedBump = getSpeedBumpIndex() == 0;
+ }
viewState.hideSensitive = false;
viewState.setXTranslation(getTranslationX());
viewState.hasItemsInStableShelf = lastViewState.inShelf;
@@ -271,6 +275,7 @@ public class NotificationShelf extends ActivatableNotificationView {
}
private int getSpeedBumpIndex() {
+ NotificationIconContainerRefactor.assertInLegacyMode();
return mHostLayout.getSpeedBumpIndex();
}
@@ -280,6 +285,7 @@ public class NotificationShelf extends ActivatableNotificationView {
*/
@VisibleForTesting
public void updateActualWidth(float fractionToShade, float shortestWidth) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
final float actualWidth = mAmbientState.isOnKeyguard()
? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
: getWidth();
@@ -290,6 +296,15 @@ public class NotificationShelf extends ActivatableNotificationView {
mActualWidth = actualWidth;
}
+ private void setActualWidth(float actualWidth) {
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
+ setBackgroundWidth((int) actualWidth);
+ if (mShelfIcons != null) {
+ mShelfIcons.setActualLayoutWidth((int) actualWidth);
+ }
+ mActualWidth = actualWidth;
+ }
+
@Override
public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
super.getBoundsOnScreen(outRect, clipToParent);
@@ -467,12 +482,26 @@ public class NotificationShelf extends ActivatableNotificationView {
final float fractionToShade = Interpolators.STANDARD.getInterpolation(
mAmbientState.getFractionToShade());
- final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
- updateActualWidth(fractionToShade, shortestWidth);
+
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ if (mAmbientState.isOnKeyguard()) {
+ float numViews = MathUtils.min(numViewsInShelf, mMaxIconsOnLockscreen + 1);
+ float shortestWidth = mShelfIcons.calculateWidthFor(numViews);
+ float actualWidth = MathUtils.lerp(shortestWidth, getWidth(), fractionToShade);
+ setActualWidth(actualWidth);
+ } else {
+ setActualWidth(getWidth());
+ }
+ } else {
+ final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
+ updateActualWidth(fractionToShade, shortestWidth);
+ }
// TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
- mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex());
+ if (!NotificationIconContainerRefactor.isEnabled()) {
+ mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex());
+ }
mShelfIcons.calculateIconXTranslations();
mShelfIcons.applyIconStates();
for (int i = 0; i < getHostLayoutChildCount(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 2cd55609a749..ef87406036b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -269,8 +269,7 @@ constructor(
fun isDateWeatherDecoupled(): Boolean {
execution.assertIsMainThread()
- return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
- datePlugin != null && weatherPlugin != null
+ return datePlugin != null && weatherPlugin != null
}
fun isWeatherEnabled(): Boolean {
@@ -501,8 +500,8 @@ constructor(
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
- if (isDateWeatherDecoupled()) {
- return t.featureType != SmartspaceTarget.FEATURE_WEATHER
+ if (isDateWeatherDecoupled() && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
+ return false
}
if (!showNotifications) {
return t.featureType == SmartspaceTarget.FEATURE_WEATHER
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 64970e456c1d..fa2748c1dc77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -16,17 +16,19 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.app.tracing.traceSection
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
@@ -40,6 +42,7 @@ internal constructor(
private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
private val notificationIconAreaController: NotificationIconAreaController,
private val renderListInteractor: RenderNotificationListInteractor,
+ private val activeNotificationsInteractor: ActiveNotificationsInteractor,
) : Coordinator {
override fun attach(pipeline: NotifPipeline) {
@@ -49,8 +52,14 @@ internal constructor(
fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
traceSection("StackCoordinator.onAfterRenderList") {
- controller.setNotifStats(calculateNotifStats(entries))
- if (NotificationIconContainerRefactor.isEnabled) {
+ val notifStats = calculateNotifStats(entries)
+ if (FooterViewRefactor.isEnabled) {
+ activeNotificationsInteractor.setNotifStats(notifStats)
+ }
+ // TODO(b/293167744): This shouldn't be done if the footer flag is on, once the footer
+ // visibility is handled in the new stack.
+ controller.setNotifStats(notifStats)
+ if (NotificationIconContainerRefactor.isEnabled || FooterViewRefactor.isEnabled) {
renderListInteractor.setRenderedList(entries)
} else {
notificationIconAreaController.updateNotificationIcons(entries)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
index fde4ecb7bcaa..a37937a6c495 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
@@ -26,6 +26,7 @@ interface NotifStackController {
/** Data provided to the NotificationRootController whenever the pipeline runs */
data class NotifStats(
+ // TODO(b/293167744): The count can be removed from here when we remove the FooterView flag.
val numActiveNotifs: Int,
val hasNonClearableAlertingNotifs: Boolean,
val hasClearableAlertingNotifs: Boolean,
@@ -33,17 +34,16 @@ data class NotifStats(
val hasClearableSilentNotifs: Boolean
) {
companion object {
- @JvmStatic
- val empty = NotifStats(0, false, false, false, false)
+ @JvmStatic val empty = NotifStats(0, false, false, false, false)
}
}
/**
* An implementation of NotifStackController which provides default, no-op implementations of each
- * method. This is used by ArcSystemUI so that that implementation can opt-in to overriding
- * methods, rather than forcing us to add no-op implementations in their implementation every time
- * a method is added.
+ * method. This is used by ArcSystemUI so that that implementation can opt-in to overriding methods,
+ * rather than forcing us to add no-op implementations in their implementation every time a method
+ * is added.
*/
open class DefaultNotifStackController @Inject constructor() : NotifStackController {
override fun setNotifStats(stats: NotifStats) {}
-} \ No newline at end of file
+}
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 860ad630be0c..0f14135f1d4c 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
@@ -64,6 +64,10 @@ import com.android.systemui.statusbar.notification.init.NotificationsControllerS
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProviderModule;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderImpl;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
@@ -268,4 +272,24 @@ public interface NotificationsModule {
/** */
@Binds
NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
+
+ /** */
+ @Provides
+ @SysUISingleton
+ static VisualInterruptionDecisionProvider provideVisualInterruptionDecisionProvider(
+ Provider<NotificationInterruptStateProviderImpl> oldImplProvider,
+ Provider<VisualInterruptionDecisionProviderImpl> newImplProvider) {
+ if (VisualInterruptionRefactor.isEnabled()) {
+ return newImplProvider.get();
+ } else {
+ return new NotificationInterruptStateProviderWrapper(oldImplProvider.get());
+ }
+ }
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(VisualInterruptionDecisionProvider.class)
+ CoreStartable startVisualInterruptionDecisionProvider(
+ VisualInterruptionDecisionProvider provider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 12ee54d4977d..5ed82cc1ed5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
@@ -37,6 +38,9 @@ class ActiveNotificationListRepository @Inject constructor() {
/** Are any already-seen notifications currently filtered out of the active list? */
val hasFilteredOutSeenNotifications = MutableStateFlow(false)
+
+ /** Stats about the list of notifications attached to the shade */
+ val notifStats = MutableStateFlow(NotifStats.empty)
}
/** Represents the notification list, comprised of groups and individual notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 85ba205d3a0a..31893b402e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,17 +15,19 @@
package com.android.systemui.statusbar.notification.domain.interactor
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class ActiveNotificationsInteractor
@Inject
constructor(
- repository: ActiveNotificationListRepository,
+ private val repository: ActiveNotificationListRepository,
) {
/** Notifications actively presented to the user in the notification stack, in order. */
val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
@@ -40,4 +42,25 @@ constructor(
}
}
}
+
+ /** Are any notifications being actively presented in the notification stack? */
+ val areAnyNotificationsPresent: Flow<Boolean> =
+ repository.activeNotifications.map { it.renderList.isNotEmpty() }.distinctUntilChanged()
+
+ /**
+ * The same as [areAnyNotificationsPresent], but without flows, for easy access in synchronous
+ * code.
+ */
+ val areAnyNotificationsPresentValue: Boolean
+ get() = repository.activeNotifications.value.renderList.isNotEmpty()
+
+ /** Are there are any notifications that can be cleared by the "Clear all" button? */
+ val hasClearableNotifications: Flow<Boolean> =
+ repository.notifStats
+ .map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
+ .distinctUntilChanged()
+
+ fun setNotifStats(notifStats: NotifStats) {
+ repository.notifStats.value = notifStats
+ }
}
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 10a43d53353d..3184d5efe5cf 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
@@ -46,6 +46,7 @@ import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;
+import java.util.function.Consumer;
public class FooterView extends StackScrollerDecorView {
private static final String TAG = "FooterView";
@@ -63,9 +64,13 @@ public class FooterView extends StackScrollerDecorView {
private String mSeenNotifsFilteredText;
private Drawable mSeenNotifsFilteredIcon;
+ private @StringRes int mClearAllButtonTextId;
+ private @StringRes int mClearAllButtonDescriptionId;
private @StringRes int mMessageStringId;
private @DrawableRes int mMessageIconId;
+ private OnClickListener mClearAllButtonClickListener;
+
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -84,12 +89,18 @@ public class FooterView extends StackScrollerDecorView {
return isSecondaryVisible();
}
+ /** See {@link this#setClearAllButtonVisible(boolean, boolean, Consumer)}. */
+ public void setClearAllButtonVisible(boolean visible, boolean animate) {
+ setClearAllButtonVisible(visible, animate, /* onAnimationEnded = */ null);
+ }
+
/**
* Set the visibility of the "Clear all" button to {@code visible}. Animate the change if
* {@code animate} is true.
*/
- public void setClearAllButtonVisible(boolean visible, boolean animate) {
- setSecondaryVisible(visible, animate);
+ public void setClearAllButtonVisible(boolean visible, boolean animate,
+ Consumer<Boolean> onAnimationEnded) {
+ setSecondaryVisible(visible, animate, onAnimationEnded);
}
@Override
@@ -106,6 +117,42 @@ public class FooterView extends StackScrollerDecorView {
});
}
+ /** Set the text label for the "Clear all" button. */
+ public void setClearAllButtonText(@StringRes int textId) {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
+ if (mClearAllButtonTextId == textId) {
+ return; // nothing changed
+ }
+ mClearAllButtonTextId = textId;
+ updateClearAllButtonText();
+ }
+
+ private void updateClearAllButtonText() {
+ if (mClearAllButtonTextId == 0) {
+ return; // not initialized yet
+ }
+ mClearAllButton.setText(getContext().getString(mClearAllButtonTextId));
+ }
+
+ /** Set the accessibility content description for the "Clear all" button. */
+ public void setClearAllButtonDescription(@StringRes int contentDescriptionId) {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ return;
+ }
+ if (mClearAllButtonDescriptionId == contentDescriptionId) {
+ return; // nothing changed
+ }
+ mClearAllButtonDescriptionId = contentDescriptionId;
+ updateClearAllButtonDescription();
+ }
+
+ private void updateClearAllButtonDescription() {
+ if (mClearAllButtonDescriptionId == 0) {
+ return; // not initialized yet
+ }
+ mClearAllButton.setContentDescription(getContext().getString(mClearAllButtonDescriptionId));
+ }
+
/** Set the string for a message to be shown instead of the buttons. */
public void setMessageString(@StringRes int messageId) {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
@@ -181,6 +228,10 @@ public class FooterView extends StackScrollerDecorView {
/** Set onClickListener for the clear all (end) button. */
public void setClearAllButtonClickListener(OnClickListener listener) {
+ if (FooterViewRefactor.isEnabled()) {
+ if (mClearAllButtonClickListener == listener) return;
+ mClearAllButtonClickListener = listener;
+ }
mClearAllButton.setOnClickListener(listener);
}
@@ -214,7 +265,28 @@ public class FooterView extends StackScrollerDecorView {
mManageButton.setText(mManageNotificationText);
mManageButton.setContentDescription(mManageNotificationText);
}
- if (!FooterViewRefactor.isEnabled()) {
+ if (FooterViewRefactor.isEnabled()) {
+ updateClearAllButtonText();
+ updateClearAllButtonDescription();
+
+ updateMessageString();
+ updateMessageIcon();
+ } else {
+ // NOTE: Prior to the refactor, `updateResources` set the class properties to the right
+ // string values. It was always being called together with `updateContent`, which
+ // deals with actually associating those string values with the correct views
+ // (buttons or text).
+ // In the new code, the resource IDs are being set in the view binder (through
+ // setMessageString and similar setters). The setters themselves now deal with
+ // updating both the resource IDs and the views where appropriate (as in, calling
+ // `updateMessageString` when the resource ID changes). This eliminates the need for
+ // `updateResources`, which will eventually be removed. There are, however, still
+ // situations in which we want to update the views even if the resource IDs didn't
+ // change, such as configuration changes.
+ mClearAllButton.setText(R.string.clear_all_notifications_text);
+ mClearAllButton.setContentDescription(
+ mContext.getString(R.string.accessibility_clear_all));
+
mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
mSeenNotifsFooterTextView
.setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
@@ -230,16 +302,8 @@ public class FooterView extends StackScrollerDecorView {
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateColors();
- mClearAllButton.setText(R.string.clear_all_notifications_text);
- mClearAllButton.setContentDescription(
- mContext.getString(R.string.accessibility_clear_all));
updateResources();
updateContent();
-
- if (FooterViewRefactor.isEnabled()) {
- updateMessageString();
- updateMessageIcon();
- }
}
/**
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 6d8234371b65..0299114e0afc 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
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.notification.footer.ui.viewbinder
+import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
@@ -28,9 +32,31 @@ object FooterViewBinder {
fun bind(
footer: FooterView,
viewModel: FooterViewModel,
+ clearAllNotifications: View.OnClickListener,
): DisposableHandle {
+ // Listen for changes when the view is attached.
return footer.repeatWhenAttached {
- // Listen for changes when the view is attached.
+ lifecycleScope.launch {
+ viewModel.clearAllButton.collect { button ->
+ if (button.isVisible.isAnimating) {
+ footer.setClearAllButtonVisible(
+ button.isVisible.value,
+ /* animate = */ true,
+ ) { _ ->
+ button.isVisible.stopAnimating()
+ }
+ } else {
+ footer.setClearAllButtonVisible(
+ button.isVisible.value,
+ /* animate = */ false,
+ )
+ }
+ footer.setClearAllButtonText(button.labelId)
+ footer.setClearAllButtonDescription(button.accessibilityDescriptionId)
+ footer.setClearAllButtonClickListener(clearAllNotifications)
+ }
+ }
+
lifecycleScope.launch {
viewModel.message.collect { message ->
footer.setFooterLabelVisible(message.visible)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
new file mode 100644
index 000000000000..ea5abeff7042
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.footer.ui.viewmodel
+
+import android.annotation.StringRes
+import com.android.systemui.util.ui.AnimatedValue
+
+data class FooterButtonViewModel(
+ @StringRes val labelId: Int,
+ @StringRes val accessibilityDescriptionId: Int,
+ val isVisible: AnimatedValue<Boolean>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 739048531afb..721bea1086e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -18,21 +18,50 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.toAnimatedValueFlow
import dagger.Module
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** ViewModel for [FooterView]. */
-class FooterViewModel(seenNotificationsInteractor: SeenNotificationsInteractor) {
- init {
- /* Check if */ FooterViewRefactor.isUnexpectedlyInLegacyMode()
- }
+class FooterViewModel(
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ seenNotificationsInteractor: SeenNotificationsInteractor,
+ shadeInteractor: ShadeInteractor,
+) {
+ val clearAllButton: Flow<FooterButtonViewModel> =
+ activeNotificationsInteractor.hasClearableNotifications
+ .sample(
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
+ val shouldAnimate = isShadeFullyExpanded && animationsEnabled
+ AnimatableEvent(hasClearableNotifications, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ .map { visible ->
+ FooterButtonViewModel(
+ labelId = R.string.clear_all_notifications_text,
+ accessibilityDescriptionId = R.string.accessibility_clear_all,
+ isVisible = visible,
+ )
+ }
val message: Flow<FooterMessageViewModel> =
seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs ->
@@ -49,10 +78,18 @@ object FooterViewModelModule {
@Provides
@SysUISingleton
fun provideOptional(
+ activeNotificationsInteractor: Provider<ActiveNotificationsInteractor>,
seenNotificationsInteractor: Provider<SeenNotificationsInteractor>,
+ shadeInteractor: Provider<ShadeInteractor>,
): Optional<FooterViewModel> {
return if (FooterViewRefactor.isEnabled) {
- Optional.of(FooterViewModel(seenNotificationsInteractor.get()))
+ Optional.of(
+ FooterViewModel(
+ activeNotificationsInteractor.get(),
+ seenNotificationsInteractor.get(),
+ shadeInteractor.get()
+ )
+ )
} else {
Optional.empty()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 2ea7f61aee4c..b1e52afd3d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -23,23 +23,25 @@ import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
import com.android.internal.policy.SystemBarUtils
+import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.icon.IconPack
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
-import com.android.systemui.util.children
import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.stateFlow
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
@@ -62,11 +64,18 @@ object NotificationIconContainerViewBinder {
viewModel: NotificationIconContainerShelfViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: ShelfNotificationIconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
- viewModel.icons.bindIcons(view, configuration, configurationController, viewStore)
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ configurationController,
+ notifyBindingFailures = { failureTracker.shelfFailures = it },
+ viewStore,
+ )
}
}
}
@@ -77,18 +86,20 @@ object NotificationIconContainerViewBinder {
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: StatusBarNotificationIconViewStore,
): DisposableHandle {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
return view.repeatWhenAttached {
lifecycleScope.run {
launch {
- val iconColors =
+ val iconColors: Flow<NotificationIconColors> =
viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
viewModel.icons.bindIcons(
view,
configuration,
configurationController,
+ notifyBindingFailures = { failureTracker.statusBarFailures = it },
viewStore,
) { _, sbiv ->
StatusBarIconViewBinder.bindIconColors(
@@ -110,15 +121,18 @@ object NotificationIconContainerViewBinder {
viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
+ view.setUseIncreasedIconScale(true)
launch {
viewModel.icons.bindIcons(
view,
configuration,
configurationController,
+ notifyBindingFailures = { failureTracker.aodFailures = it },
viewStore,
) { _, sbiv ->
viewModel.bindAodStatusBarIconView(sbiv, configuration)
@@ -176,7 +190,7 @@ object NotificationIconContainerViewBinder {
}
/**
- * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children].
+ * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s children.
*
* [bindIcon] will be invoked to bind a child [StatusBarIconView] to an icon associated with the
* given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
@@ -186,6 +200,7 @@ object NotificationIconContainerViewBinder {
view: NotificationIconContainer,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
): Unit = coroutineScope {
@@ -207,113 +222,145 @@ object NotificationIconContainerViewBinder {
->
FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
}
-
- launch {
- layoutParams.collect { params: FrameLayout.LayoutParams ->
- for (child in view.children) {
- child.layoutParams = params
- }
- }
- }
-
- val iconBindings = mutableMapOf<String, Job>()
+ val failedBindings = mutableSetOf<String>()
+ val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
var prevIcons = NotificationIconsViewData()
- sample(layoutParams, ::Pair).collect {
- (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
- ->
+ collect { iconsData: NotificationIconsViewData ->
val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
prevIcons = iconsData
- val replacingIcons =
- iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, v) ->
- viewStore.iconView(v.notifKey).statusBarIcon
+ // Lookup 1:1 group icon replacements
+ val replacingIcons: ArrayMap<String, StatusBarIcon> =
+ iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, notifKey) ->
+ boundViewsByNotifKey[notifKey]?.first?.statusBarIcon
}
- view.setReplacingIcons(replacingIcons)
-
- val childrenByNotifKey: Map<String, StatusBarIconView> =
- view.children.filterIsInstance<StatusBarIconView>().associateByTo(ArrayMap()) {
- it.notification.key
+ view.withIconReplacements(replacingIcons) {
+ // Remove and unbind.
+ for (notifKey in iconsDiff.removed) {
+ failedBindings.remove(notifKey)
+ val (child, job) = boundViewsByNotifKey.remove(notifKey) ?: continue
+ view.removeView(child)
+ job.cancel()
}
- iconsDiff.removed
- .mapNotNull { key -> childrenByNotifKey[key]?.let { key to it } }
- .forEach { (key, child) ->
- view.removeView(child)
- iconBindings.remove(key)?.cancel()
+ // Add and bind.
+ val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings.toList()
+ for ((idx, notifKey) in toAdd.withIndex()) {
+ // Lookup the StatusBarIconView from the store.
+ val sbiv = viewStore.iconView(notifKey)
+ if (sbiv == null) {
+ failedBindings.add(notifKey)
+ continue
+ }
+ failedBindings.remove(notifKey)
+ // The view might still be transiently added if it was just removed and added
+ // again
+ view.removeTransientView(sbiv)
+ view.addView(sbiv, idx)
+ boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
+ boundViewsByNotifKey[notifKey] =
+ Pair(
+ sbiv,
+ launch {
+ launch { layoutParams.collect { sbiv.layoutParams = it } }
+ bindIcon(notifKey, sbiv)
+ },
+ )
}
- val toAdd = iconsDiff.added.map { it.notifKey to viewStore.iconView(it.notifKey) }
- for ((i, keyAndView) in toAdd.withIndex()) {
- val (key, sbiv) = keyAndView
- // The view might still be transiently added if it was just removed
- // and added again
- view.removeTransientView(sbiv)
- view.addView(sbiv, i, layoutParams)
- iconBindings.remove(key)?.cancel()
- iconBindings[key] = launch { bindIcon(key, sbiv) }
- }
+ // Set the maximum number of icons to show in the container. Any icons over this
+ // amount will render as an "overflow dot".
+ val maxIconsAmount: Int =
+ when (iconsData.limitType) {
+ LimitType.MaximumIndex -> {
+ iconsData.visibleIcons
+ .asSequence()
+ .take(iconsData.iconLimit)
+ .count { info -> info.notifKey in boundViewsByNotifKey }
+ }
+ LimitType.MaximumAmount -> {
+ iconsData.iconLimit
+ }
+ }
+ view.setMaxIconsAmount(maxIconsAmount)
+
+ // Track the binding failures so that they appear in dumpsys.
+ notifyBindingFailures(failedBindings)
- view.setChangingViewPositions(true)
- // Re-sort notification icons
- val childCount = view.childCount
- for (i in 0 until childCount) {
- val actual = view.getChildAt(i)
- val expected = viewStore.iconView(iconsData.visibleKeys[i].notifKey)
- if (actual === expected) {
- continue
+ // Re-sort notification icons
+ view.changeViewPositions {
+ val expectedChildren: List<StatusBarIconView> =
+ iconsData.visibleIcons.mapNotNull {
+ boundViewsByNotifKey[it.notifKey]?.first
+ }
+ val childCount = view.childCount
+ for (i in 0 until childCount) {
+ val actual = view.getChildAt(i)
+ val expected = expectedChildren[i]
+ if (actual === expected) {
+ continue
+ }
+ view.removeView(expected)
+ view.addView(expected, i)
+ }
}
- view.removeView(expected)
- view.addView(expected, i)
}
- view.setChangingViewPositions(false)
-
- view.setReplacingIcons(null)
+ // Recalculate all icon positions, to reflect our updates.
+ view.calculateIconXTranslations()
}
}
+ /**
+ * Track which groups are being replaced with a different icon instance, but with the same
+ * visual icon. This prevents a weird animation where it looks like an icon disappears and
+ * reappears unchanged.
+ */
+ // TODO(b/305739416): Ideally we wouldn't swap out the StatusBarIconView at all, and instead use
+ // a single SBIV instance for the group. Then this whole concept can go away.
+ private inline fun <R> NotificationIconContainer.withIconReplacements(
+ replacements: ArrayMap<String, StatusBarIcon>,
+ block: () -> R
+ ): R {
+ setReplacingIcons(replacements)
+ return block().also { setReplacingIcons(null) }
+ }
+
+ /**
+ * Any invocations of [NotificationIconContainer.addView] /
+ * [NotificationIconContainer.removeView] inside of [block] will not cause a new add / remove
+ * animation.
+ */
+ private inline fun <R> NotificationIconContainer.changeViewPositions(block: () -> R): R {
+ setChangingViewPositions(true)
+ return block().also { setChangingViewPositions(false) }
+ }
+
/** External storage for [StatusBarIconView] instances. */
fun interface IconViewStore {
- fun iconView(key: String): StatusBarIconView
+ fun iconView(key: String): StatusBarIconView?
}
@ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
}
/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
-class ShelfNotificationIconViewStore
-@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.shelfIcon ?: error("No shelf IconView found for key: $key")
- }
-}
+class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
/** [IconViewStore] for the always-on display. */
class AlwaysOnDisplayNotificationIconViewStore
@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.aodIcon ?: error("No AOD IconView found for key: $key")
- }
-}
+constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
/** [IconViewStore] for the status bar. */
-class StatusBarNotificationIconViewStore
-@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.statusBarIcon ?: error("No status bar IconView found for key: $key")
+class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
+
+private fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
+ IconViewStore { key ->
+ getEntry(key)?.icons?.let(block)
}
-}
private val View.viewBounds: Rect
get() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt
new file mode 100644
index 000000000000..0c114a2ac55d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.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.statusbar.notification.icon.ui.viewbinder
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import dagger.Binds
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import java.io.PrintWriter
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarIconViewBindingFailureTracker @Inject constructor() : CoreStartable {
+
+ var aodFailures: Collection<String> = emptyList()
+ var statusBarFailures: Collection<String> = emptyList()
+ var shelfFailures: Collection<String> = emptyList()
+
+ // TODO(b/310681665): Ideally we wouldn't need to implement CoreStartable at all, and could just
+ // @Binds @IntoSet the Dumpable.
+ override fun start() {
+ // no-op, we're just using this as a dumpable
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ if (!NotificationIconContainerRefactor.isEnabled) return
+ pw.asIndenting().run {
+ printCollection("AOD Icon binding failures:", aodFailures)
+ printCollection("Status Bar Icon binding failures:", statusBarFailures)
+ printCollection("Shelf Icon binding failures:", shelfFailures)
+ }
+ }
+
+ @dagger.Module
+ interface Module {
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBarIconViewBindingFailureTracker::class)
+ fun bindStartable(impl: StatusBarIconViewBindingFailureTracker): CoreStartable
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index b11eca2c7d93..9cb60d56f277 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -15,10 +15,13 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import android.content.res.Resources
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
import javax.inject.Inject
@@ -35,9 +38,12 @@ constructor(
iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ @Main resources: Resources,
shadeInteractor: ShadeInteractor,
) {
+ private val maxIcons = resources.getInteger(R.integer.max_notif_icons_on_aod)
+
/** Are changes to the icon container animated? */
val areContainerChangesAnimated: Flow<Boolean> =
combine(
@@ -67,7 +73,8 @@ constructor(
val icons: Flow<NotificationIconsViewData> =
iconsInteractor.aodNotifs.map { entries ->
NotificationIconsViewData(
- visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
+ visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
+ iconLimit = maxIcons,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index 1560106bfadb..8484fdc92d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -29,8 +30,23 @@ constructor(
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
interactor.filteredNotifSet().map { entries ->
+ var firstAmbient = 0
+ val visibleKeys = buildList {
+ for (entry in entries) {
+ entry.toIconInfo(entry.shelfIcon)?.let { info ->
+ add(info)
+ // NOTE: we assume that all ambient notifications will be at the end of the
+ // list
+ if (!entry.isAmbient) {
+ firstAmbient++
+ }
+ }
+ }
+ }
NotificationIconsViewData(
- visibleKeys = entries.mapNotNull { it.toIconInfo(it.shelfIcon) },
+ visibleKeys,
+ iconLimit = firstAmbient,
+ limitType = LimitType.MaximumIndex,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 82626acc4b04..af37e49c10f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -15,9 +15,12 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import android.content.res.Resources
import android.graphics.Rect
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
@@ -31,6 +34,7 @@ import com.android.systemui.util.ui.toAnimatedValueFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -43,9 +47,12 @@ constructor(
headsUpIconInteractor: HeadsUpNotificationIconInteractor,
keyguardInteractor: KeyguardInteractor,
notificationsInteractor: ActiveNotificationsInteractor,
+ @Main resources: Resources,
shadeInteractor: ShadeInteractor,
) {
+ private val maxIcons = resources.getInteger(R.integer.max_notif_static_icons)
+
/** Are changes to the icon container animated? */
val animationsEnabled: Flow<Boolean> =
combine(
@@ -76,26 +83,26 @@ constructor(
val icons: Flow<NotificationIconsViewData> =
iconsInteractor.statusBarNotifs.map { entries ->
NotificationIconsViewData(
- visibleKeys = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
+ visibleIcons = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
+ iconLimit = maxIcons,
)
}
/** An Icon to show "isolated" in the IconContainer. */
val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
headsUpIconInteractor.isolatedNotification
+ .combine(icons) { isolatedNotif, iconsViewData ->
+ isolatedNotif?.let {
+ iconsViewData.visibleIcons.firstOrNull { it.notifKey == isolatedNotif }
+ }
+ }
.pairwise(initialValue = null)
- .sample(combine(icons, shadeInteractor.shadeExpansion, ::Pair)) {
- (prev, isolatedNotif),
- (iconsViewData, shadeExpansion),
- ->
- val iconInfo =
- isolatedNotif?.let {
- iconsViewData.visibleKeys.firstOrNull { it.notifKey == isolatedNotif }
- }
+ .distinctUntilChanged()
+ .sample(shadeInteractor.shadeExpansion) { (prev, iconInfo), shadeExpansion ->
val animate =
when {
- isolatedNotif == prev -> false
- isolatedNotif == null || prev == null -> shadeExpansion == 0f
+ iconInfo?.notifKey == prev?.notifKey -> false
+ iconInfo == null || prev == null -> shadeExpansion == 0f
else -> false
}
AnimatableEvent(iconInfo, animate)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
index 867be84f3a12..e8756d031e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
@@ -23,16 +23,33 @@ import com.android.systemui.util.kotlin.mapValuesNotNullTo
/** Encapsulates the collection of notification icons present on the device. */
data class NotificationIconsViewData(
/** Icons that are visible in the container. */
- val visibleKeys: List<NotificationIconInfo> = emptyList(),
- /** Keys of icons that are "behind" the overflow dot. */
- val collapsedKeys: Set<String> = emptySet(),
- /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */
- val forceShowDot: Boolean = false,
+ val visibleIcons: List<NotificationIconInfo> = emptyList(),
+ /** Limit applied to the [visibleIcons]; can be interpreted different based on [limitType]. */
+ val iconLimit: Int = visibleIcons.size,
+ /** How [iconLimit] is applied to [visibleIcons]. */
+ val limitType: LimitType = LimitType.MaximumAmount,
) {
+ // TODO(b/305739416): This can be removed once we are no longer looking up the StatusBarIconView
+ // instances from outside of the view-binder layer. Ideally, we would just use MaximumAmount,
+ // and apply it directly to the list of visibleIcons by truncating the list to that amount.
+ // At the time of this writing, we cannot do that because looking up the SBIV can fail, and so
+ // we need additional icons to fall-back to.
+ /** Determines how a limit to the icons is to be applied. */
+ enum class LimitType {
+ /** The [Int] limit is a maximum amount of icons to be displayed. */
+ MaximumAmount,
+ /**
+ * The [Int] limit is a maximum index into the
+ * [list of visible icons][NotificationIconsViewData.visibleIcons] to be displayed; any
+ * icons beyond that index should be omitted.
+ */
+ MaximumIndex,
+ }
+
/** The difference between two [NotificationIconsViewData]s. */
data class Diff(
/** Icons added in the newer dataset. */
- val added: List<NotificationIconInfo> = emptyList(),
+ val added: List<String> = emptyList(),
/** Icons removed from the older dataset. */
val removed: List<String> = emptyList(),
/**
@@ -44,7 +61,7 @@ data class NotificationIconsViewData(
* same group. A view binder can use this information for special animations for this
* specific change.
*/
- val groupReplacements: Map<String, NotificationIconInfo> = emptyMap(),
+ val groupReplacements: Map<String, String> = emptyMap(),
)
companion object {
@@ -56,23 +73,20 @@ data class NotificationIconsViewData(
new: NotificationIconsViewData,
prev: NotificationIconsViewData
): Diff {
- val added: List<NotificationIconInfo> =
- new.visibleKeys.filter {
- it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey }
- }
+ val prevKeys = prev.visibleIcons.asSequence().map { it.notifKey }.toSet()
+ val newKeys = new.visibleIcons.asSequence().map { it.notifKey }.toSet()
+ val added: List<String> = newKeys.mapNotNull { key -> key.takeIf { it !in prevKeys } }
val removed: List<NotificationIconInfo> =
- prev.visibleKeys.filter {
- it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey }
- }
+ prev.visibleIcons.filter { it.notifKey !in newKeys }
val groupsToShow: Set<IconGroupInfo> =
- new.visibleKeys.asSequence().map { it.groupInfo }.toSet()
- val replacements: ArrayMap<String, NotificationIconInfo> =
+ new.visibleIcons.asSequence().map { it.groupInfo }.toSet()
+ val replacements: ArrayMap<String, String> =
removed
.asSequence()
.filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow }
.groupBy { it.groupInfo.groupKey }
.mapValuesNotNullTo(ArrayMap()) { (_, vs) ->
- vs.takeIf { it.size == 1 }?.get(0)
+ vs.takeIf { it.size == 1 }?.get(0)?.notifKey
}
return Diff(added, removed.map { it.notifKey }, replacements)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 538be142b8f2..8e824423973f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -32,6 +32,7 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
@@ -43,9 +44,9 @@ import com.android.systemui.util.time.SystemClock
class PeekDisabledSuppressor(
private val globalSettings: GlobalSettings,
private val headsUpManager: HeadsUpManager,
- private val logger: NotificationInterruptLogger,
+ private val logger: VisualInterruptionDecisionLogger,
@Main private val mainHandler: Handler,
-) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") {
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek disabled by global setting") {
private var isEnabled = false
override fun shouldSuppress(): Boolean = !isEnabled
@@ -87,16 +88,13 @@ class PeekDisabledSuppressor(
class PulseDisabledSuppressor(
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val userTracker: UserTracker,
-) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") {
+) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse disabled by user setting") {
override fun shouldSuppress(): Boolean =
!ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId)
}
class PulseBatterySaverSuppressor(private val batteryController: BatteryController) :
- VisualInterruptionCondition(
- types = setOf(PULSE),
- reason = "pulsing disabled by battery saver"
- ) {
+ VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse disabled by battery saver") {
override fun shouldSuppress() = batteryController.isAodPowerSave()
}
@@ -128,14 +126,14 @@ class PeekDndSuppressor() :
}
class PeekNotImportantSuppressor() :
- VisualInterruptionFilter(types = setOf(PEEK), reason = "not important") {
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "importance < HIGH") {
override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_HIGH
}
class PeekDeviceNotInUseSuppressor(
private val powerManager: PowerManager,
private val statusBarStateController: StatusBarStateController
-) : VisualInterruptionCondition(types = setOf(PEEK), reason = "not in use") {
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "device not in use") {
override fun shouldSuppress() =
when {
!powerManager.isScreenOn || statusBarStateController.isDreaming -> true
@@ -144,7 +142,11 @@ class PeekDeviceNotInUseSuppressor(
}
class PeekOldWhenSuppressor(private val systemClock: SystemClock) :
- VisualInterruptionFilter(types = setOf(PEEK), reason = "old when") {
+ VisualInterruptionFilter(
+ types = setOf(PEEK),
+ reason = "has old `when`",
+ uiEventId = HUN_SUPPRESSED_OLD_WHEN
+ ) {
private fun whenAge(entry: NotificationEntry) =
systemClock.currentTimeMillis() - entry.sbn.notification.`when`
@@ -165,21 +167,21 @@ class PeekOldWhenSuppressor(private val systemClock: SystemClock) :
}
class PulseEffectSuppressor() :
- VisualInterruptionFilter(types = setOf(PULSE), reason = "ambient effect suppressed") {
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "suppressed by DND") {
override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressAmbient()
}
class PulseLockscreenVisibilityPrivateSuppressor() :
VisualInterruptionFilter(
types = setOf(PULSE),
- reason = "notification hidden on lock screen by override"
+ reason = "hidden by lockscreen visibility override"
) {
override fun shouldSuppress(entry: NotificationEntry) =
entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE
}
class PulseLowImportanceSuppressor() :
- VisualInterruptionFilter(types = setOf(PULSE), reason = "importance less than DEFAULT") {
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "importance < DEFAULT") {
override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_DEFAULT
}
@@ -198,12 +200,17 @@ class HunJustLaunchedFsiSuppressor() :
}
class BubbleNotAllowedSuppressor() :
- VisualInterruptionFilter(types = setOf(BUBBLE), reason = "not allowed") {
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "cannot bubble") {
override fun shouldSuppress(entry: NotificationEntry) = !entry.canBubble()
}
+class BubbleAppSuspendedSuppressor :
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "app is suspended") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.ranking.isSuspended
+}
+
class BubbleNoMetadataSuppressor() :
- VisualInterruptionFilter(types = setOf(BUBBLE), reason = "no bubble metadata") {
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "has no or invalid bubble metadata") {
private fun isValidMetadata(metadata: BubbleMetadata?) =
metadata != null && (metadata.intent != null || metadata.shortcutId != null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
new file mode 100644
index 000000000000..2707ed83b74e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.interruption
+
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.os.PowerManager
+import com.android.internal.logging.UiEventLogger.UiEventEnum
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+
+class FullScreenIntentDecisionProvider(
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val keyguardStateController: KeyguardStateController,
+ private val powerManager: PowerManager,
+ private val statusBarStateController: StatusBarStateController
+) {
+ interface Decision {
+ val shouldFsi: Boolean
+ val wouldFsiWithoutDnd: Boolean
+ val logReason: String
+ val shouldLog: Boolean
+ val isWarning: Boolean
+ val uiEventId: UiEventEnum?
+ val eventLogData: EventLogData?
+ }
+
+ private enum class DecisionImpl(
+ override val shouldFsi: Boolean,
+ override val logReason: String,
+ override val wouldFsiWithoutDnd: Boolean = shouldFsi,
+ val supersedesDnd: Boolean = false,
+ override val shouldLog: Boolean = true,
+ override val isWarning: Boolean = false,
+ override val uiEventId: UiEventEnum? = null,
+ override val eventLogData: EventLogData? = null
+ ) : Decision {
+ NO_FSI_NO_FULL_SCREEN_INTENT(
+ false,
+ "no full-screen intent",
+ supersedesDnd = true,
+ shouldLog = false
+ ),
+ NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true),
+ NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"),
+ NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(
+ false,
+ "suppressive group alert behavior",
+ isWarning = true,
+ uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
+ eventLogData = EventLogData("231322873", "groupAlertBehavior")
+ ),
+ NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(
+ false,
+ "suppressive bubble metadata",
+ isWarning = true,
+ uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA,
+ eventLogData = EventLogData("274759612", "bubbleMetadata")
+ ),
+ NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"),
+ FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"),
+ FSI_DEVICE_DREAMING(true, "device is dreaming"),
+ FSI_KEYGUARD_SHOWING(true, "keyguard is showing"),
+ NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"),
+ FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"),
+ FSI_LOCKED_SHADE(true, "locked shade"),
+ FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"),
+ NO_FSI_NO_HUN_OR_KEYGUARD(
+ false,
+ "no HUN or keyguard",
+ isWarning = true,
+ uiEventId = FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD,
+ eventLogData = EventLogData("231322873", "no hun or keyguard")
+ ),
+ NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false),
+ NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true)
+ }
+
+ fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision {
+ val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp)
+
+ val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi
+ val suppressedByDnd = entry.shouldSuppressFullScreenIntent()
+
+ val reasonWithDnd =
+ when {
+ reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd
+ suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND
+ suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND
+ else -> reasonWithoutDnd
+ }
+
+ return reasonWithDnd
+ }
+
+ private fun makeDecisionWithoutDnd(
+ entry: NotificationEntry,
+ couldHeadsUp: Boolean
+ ): DecisionImpl {
+ val sbn = entry.sbn
+ val notification = sbn.notification!!
+
+ if (notification.fullScreenIntent == null) {
+ return if (entry.isStickyAndNotDemoted) {
+ NO_FSI_SHOW_STICKY_HUN
+ } else {
+ NO_FSI_NO_FULL_SCREEN_INTENT
+ }
+ }
+
+ if (entry.importance < IMPORTANCE_HIGH) {
+ return NO_FSI_NOT_IMPORTANT_ENOUGH
+ }
+
+ if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) {
+ return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+ }
+
+ val bubbleMetadata = notification.bubbleMetadata
+ if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) {
+ return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
+ }
+
+ if (entry.ranking.isSuspended) {
+ return NO_FSI_PACKAGE_SUSPENDED
+ }
+
+ if (!powerManager.isInteractive) {
+ return FSI_DEVICE_NOT_INTERACTIVE
+ }
+
+ if (statusBarStateController.isDreaming) {
+ return FSI_DEVICE_DREAMING
+ }
+
+ if (statusBarStateController.state == KEYGUARD) {
+ return FSI_KEYGUARD_SHOWING
+ }
+
+ if (couldHeadsUp) {
+ return NO_FSI_EXPECTED_TO_HUN
+ }
+
+ if (keyguardStateController.isShowing) {
+ return if (keyguardStateController.isOccluded) {
+ FSI_KEYGUARD_OCCLUDED
+ } else {
+ FSI_LOCKED_SHADE
+ }
+ }
+
+ if (!deviceProvisionedController.isDeviceProvisioned) {
+ return FSI_DEVICE_NOT_PROVISIONED
+ }
+
+ return NO_FSI_NO_HUN_OR_KEYGUARD
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 2fffd379e6f9..334e08d5a78f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -56,6 +56,14 @@ class NotificationInterruptLogger @Inject constructor(
}
}
+ fun logSuspendedAppBubble(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ }, {
+ "No bubble up: notification: app $str1 is suspended"
+ })
+ }
+
fun logNoBubbleNoMetadata(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index f2ade340fc4f..510086d4892b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -49,6 +49,7 @@ import com.android.systemui.statusbar.policy.BatteryController;
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.util.EventLog;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -81,6 +82,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private final DeviceProvisionedController mDeviceProvisionedController;
private final SystemClock mSystemClock;
private final GlobalSettings mGlobalSettings;
+ private final EventLog mEventLog;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -129,7 +131,8 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
UserTracker userTracker,
DeviceProvisionedController deviceProvisionedController,
SystemClock systemClock,
- GlobalSettings globalSettings) {
+ GlobalSettings globalSettings,
+ EventLog eventLog) {
mPowerManager = powerManager;
mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
@@ -144,6 +147,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
mDeviceProvisionedController = deviceProvisionedController;
mSystemClock = systemClock;
mGlobalSettings = globalSettings;
+ mEventLog = eventLog;
ContentObserver headsUpObserver = new ContentObserver(mainHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -200,6 +204,11 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
+ if (entry.getRanking().isSuspended()) {
+ mLogger.logSuspendedAppBubble(entry);
+ return false;
+ }
+
if (entry.getBubbleMetadata() == null
|| (entry.getBubbleMetadata().getShortcutId() == null
&& entry.getBubbleMetadata().getIntent() == null)) {
@@ -369,7 +378,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
// explicitly prevent logging for this (frequent) case
return;
case NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR:
- android.util.EventLog.writeEvent(0x534e4554, "231322873", uid,
+ mEventLog.writeEvent(0x534e4554, "231322873", uid,
"groupAlertBehavior");
mUiEventLogger.log(FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, uid,
packageName);
@@ -377,7 +386,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
decision + ": GroupAlertBehavior will prevent HUN");
return;
case NO_FSI_SUPPRESSIVE_BUBBLE_METADATA:
- android.util.EventLog.writeEvent(0x534e4554, "274759612", uid,
+ mEventLog.writeEvent(0x534e4554, "274759612", uid,
"bubbleMetadata");
mUiEventLogger.log(FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA, uid,
packageName);
@@ -385,7 +394,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
decision + ": BubbleMetadata may prevent HUN");
return;
case NO_FSI_NO_HUN_OR_KEYGUARD:
- android.util.EventLog.writeEvent(0x534e4554, "231322873", uid,
+ mEventLog.writeEvent(0x534e4554, "231322873", uid,
"no hun or keyguard");
mUiEventLogger.log(FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD, uid, packageName);
mLogger.logNoFullscreenWarning(entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
index d7f0baf4f002..16bcd43dd877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
@@ -30,6 +31,9 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti
class NotificationInterruptStateProviderWrapper(
private val wrapped: NotificationInterruptStateProvider
) : VisualInterruptionDecisionProvider {
+ init {
+ VisualInterruptionRefactor.assertInLegacyMode()
+ }
@VisibleForTesting
enum class DecisionImpl(override val shouldInterrupt: Boolean) : Decision {
@@ -62,6 +66,21 @@ class NotificationInterruptStateProviderWrapper(
wrapped.removeSuppressor(suppressor)
}
+ override fun addCondition(condition: VisualInterruptionCondition) = notValidInLegacyMode()
+
+ override fun removeCondition(condition: VisualInterruptionCondition) = notValidInLegacyMode()
+
+ override fun addFilter(filter: VisualInterruptionFilter) = notValidInLegacyMode()
+
+ override fun removeFilter(filter: VisualInterruptionFilter) = notValidInLegacyMode()
+
+ private fun notValidInLegacyMode() {
+ RefactorFlagUtils.assertOnEngBuild(
+ "This method is only implemented in VisualInterruptionDecisionProviderImpl, " +
+ "and so should only be called when FLAG_VISUAL_INTERRUPTIONS_REFACTOR is enabled."
+ )
+ }
+
override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision =
wrapped.checkHeadsUp(entry, /* log= */ false).let { DecisionImpl.of(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
new file mode 100644
index 000000000000..1470b0331359
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.interruption
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationInterruptLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.logKey
+import javax.inject.Inject
+
+class VisualInterruptionDecisionLogger
+@Inject
+constructor(@NotificationInterruptLog val buffer: LogBuffer) {
+ fun logHeadsUpFeatureChanged(isEnabled: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ { bool1 = isEnabled },
+ { "HUN feature is now ${if (bool1) "enabled" else "disabled"}" }
+ )
+ }
+
+ fun logWillDismissAll() {
+ buffer.log(TAG, INFO, {}, { "dismissing all HUNs since feature was disabled" })
+ }
+
+ fun logDecision(
+ type: String,
+ entry: NotificationEntry,
+ decision: VisualInterruptionDecisionProvider.Decision
+ ) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = type
+ bool1 = decision.shouldInterrupt
+ str2 = decision.logReason
+ str3 = entry.logKey
+ },
+ {
+ val outcome = if (bool1) "allowed" else "suppressed"
+ "$str1 $outcome: $str2 (key=$str3)"
+ }
+ )
+ }
+
+ fun logFullScreenIntentDecision(
+ entry: NotificationEntry,
+ decision: FullScreenIntentDecision,
+ warning: Boolean
+ ) {
+ buffer.log(
+ TAG,
+ if (warning) WARNING else DEBUG,
+ {
+ bool1 = decision.shouldInterrupt
+ bool2 = decision.wouldInterruptWithoutDnd
+ str1 = decision.logReason
+ str2 = entry.logKey
+ },
+ {
+ val outcome =
+ when {
+ bool1 -> "allowed"
+ bool2 -> "suppressed only by DND"
+ else -> "suppressed"
+ }
+ "FSI $outcome: $str1 (key=$str2)"
+ }
+ )
+ }
+}
+
+private const val TAG = "VisualInterruptionDecisionProvider"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index da8474e92629..93fa85d61030 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.interruption
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
@@ -25,7 +27,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
* pulsing while the device is dozing), displaying the notification as a bubble, and launching a
* full-screen intent for the notification.
*/
-interface VisualInterruptionDecisionProvider {
+interface VisualInterruptionDecisionProvider : CoreStartable {
/**
* Represents the decision to visually interrupt or not.
*
@@ -51,33 +53,77 @@ interface VisualInterruptionDecisionProvider {
val wouldInterruptWithoutDnd: Boolean
}
- /**
- * Initializes the provider.
- *
- * Must be called before any method except [addLegacySuppressor].
- */
- fun start() {}
+ /** Initializes the provider. */
+ override fun start() {}
/**
- * Adds a [component][suppressor] that can suppress visual interruptions.
+ * Adds a [NotificationInterruptSuppressor] that can suppress visual interruptions.
+ *
+ * This method may be called before [start] has been called.
*
- * This class may call suppressors in any order.
+ * This class may call suppressors, conditions, and filters in any order.
*
* @param[suppressor] the suppressor to add
*/
fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor)
/**
- * Removes a [component][suppressor] that can suppress visual interruptions.
+ * Removes a previously-added suppressor.
+ *
+ * This method may be called before [start] has been called.
*
* @param[suppressor] the suppressor to remove
*/
- fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)
+ @VisibleForTesting fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)
+
+ /**
+ * Adds a [VisualInterruptionCondition] that can suppress visual interruptions without examining
+ * individual notifications.
+ *
+ * This method may be called before [start] has been called.
+ *
+ * This class may call suppressors, conditions, and filters in any order.
+ *
+ * @param[condition] the condition to add
+ */
+ fun addCondition(condition: VisualInterruptionCondition)
+
+ /**
+ * Removes a previously-added condition.
+ *
+ * This method may be called before [start] has been called.
+ *
+ * @param[condition] the condition to remove
+ */
+ @VisibleForTesting fun removeCondition(condition: VisualInterruptionCondition)
+
+ /**
+ * Adds a [VisualInterruptionFilter] that can suppress visual interruptions based on individual
+ * notifications.
+ *
+ * This method may be called before [start] has been called.
+ *
+ * This class may call suppressors, conditions, and filters in any order.
+ *
+ * @param[filter] the filter to add
+ */
+ fun addFilter(filter: VisualInterruptionFilter)
+
+ /**
+ * Removes a previously-added filter.
+ *
+ * This method may be called before [start] has been called.
+ *
+ * @param[filter] the filter to remove
+ */
+ @VisibleForTesting fun removeFilter(filter: VisualInterruptionFilter)
/**
* Decides whether a [notification][entry] should display as heads-up or not, but does not log
* that decision.
*
+ * [start] must be called before this method can be called.
+ *
* @param[entry] the notification that this decision is about
* @return the decision to display that notification as heads-up or not
*/
@@ -93,6 +139,8 @@ interface VisualInterruptionDecisionProvider {
* If the device is dozing, the decision will consider whether the notification should "pulse"
* (wake the screen up and display the ambient view of the notification).
*
+ * [start] must be called before this method can be called.
+ *
* @see[makeUnloggedHeadsUpDecision]
*
* @param[entry] the notification that this decision is about
@@ -106,6 +154,8 @@ interface VisualInterruptionDecisionProvider {
*
* The returned decision can be logged by passing it to [logFullScreenIntentDecision].
*
+ * [start] must be called before this method can be called.
+ *
* @see[makeAndLogHeadsUpDecision]
*
* @param[entry] the notification that this decision is about
@@ -116,6 +166,8 @@ interface VisualInterruptionDecisionProvider {
/**
* Logs a previous [decision] to launch a full-screen intent or not.
*
+ * [start] must be called before this method can be called.
+ *
* @param[decision] the decision to log
*/
fun logFullScreenIntentDecision(decision: FullScreenIntentDecision)
@@ -123,6 +175,8 @@ interface VisualInterruptionDecisionProvider {
/**
* Decides whether a [notification][entry] should display as a bubble or not.
*
+ * [start] must be called before this method can be called.
+ *
* @param[entry] the notification that this decision is about
* @return the decision to display that notification as a bubble or not
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 2730683a31c9..73dd5f6e5fb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -20,17 +20,23 @@ import android.os.Handler
import android.os.PowerManager
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
import com.android.systemui.statusbar.policy.BatteryController
+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.util.EventLog
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -40,16 +46,101 @@ class VisualInterruptionDecisionProviderImpl
constructor(
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val batteryController: BatteryController,
+ deviceProvisionedController: DeviceProvisionedController,
+ private val eventLog: EventLog,
private val globalSettings: GlobalSettings,
private val headsUpManager: HeadsUpManager,
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
- private val logger: NotificationInterruptLogger,
+ keyguardStateController: KeyguardStateController,
+ private val logger: VisualInterruptionDecisionLogger,
@Main private val mainHandler: Handler,
private val powerManager: PowerManager,
private val statusBarStateController: StatusBarStateController,
private val systemClock: SystemClock,
+ private val uiEventLogger: UiEventLogger,
private val userTracker: UserTracker,
) : VisualInterruptionDecisionProvider {
+ init {
+ check(!VisualInterruptionRefactor.isUnexpectedlyInLegacyMode())
+ }
+
+ interface Loggable {
+ val uiEventId: UiEventEnum?
+ val eventLogData: EventLogData?
+ }
+
+ private class DecisionImpl(
+ override val shouldInterrupt: Boolean,
+ override val logReason: String
+ ) : Decision
+
+ private data class LoggableDecision
+ private constructor(
+ val decision: DecisionImpl,
+ override val uiEventId: UiEventEnum? = null,
+ override val eventLogData: EventLogData? = null
+ ) : Loggable {
+ companion object {
+ val unsuppressed =
+ LoggableDecision(DecisionImpl(shouldInterrupt = true, logReason = "not suppressed"))
+
+ fun suppressed(legacySuppressor: NotificationInterruptSuppressor, methodName: String) =
+ LoggableDecision(
+ DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${legacySuppressor.name}.$methodName"
+ )
+ )
+
+ fun suppressed(suppressor: VisualInterruptionSuppressor) =
+ LoggableDecision(
+ DecisionImpl(shouldInterrupt = false, logReason = suppressor.reason),
+ uiEventId = suppressor.uiEventId,
+ eventLogData = suppressor.eventLogData
+ )
+ }
+ }
+
+ private class FullScreenIntentDecisionImpl(
+ val entry: NotificationEntry,
+ private val fsiDecision: FullScreenIntentDecisionProvider.Decision
+ ) : FullScreenIntentDecision, Loggable {
+ var hasBeenLogged = false
+
+ override val shouldInterrupt
+ get() = fsiDecision.shouldFsi
+
+ override val wouldInterruptWithoutDnd
+ get() = fsiDecision.wouldFsiWithoutDnd
+
+ override val logReason
+ get() = fsiDecision.logReason
+
+ val shouldLog
+ get() = fsiDecision.shouldLog
+
+ val isWarning
+ get() = fsiDecision.isWarning
+
+ override val uiEventId
+ get() = fsiDecision.uiEventId
+
+ override val eventLogData
+ get() = fsiDecision.eventLogData
+ }
+
+ private val fullScreenIntentDecisionProvider =
+ FullScreenIntentDecisionProvider(
+ deviceProvisionedController,
+ keyguardStateController,
+ powerManager,
+ statusBarStateController
+ )
+
+ private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
+ private val conditions = mutableListOf<VisualInterruptionCondition>()
+ private val filters = mutableListOf<VisualInterruptionFilter>()
+
private var started = false
override fun start() {
@@ -68,6 +159,7 @@ constructor(
addFilter(PulseLockscreenVisibilityPrivateSuppressor())
addFilter(PulseLowImportanceSuppressor())
addFilter(BubbleNotAllowedSuppressor())
+ addFilter(BubbleAppSuspendedSuppressor())
addFilter(BubbleNoMetadataSuppressor())
addFilter(HunGroupAlertBehaviorSuppressor())
addFilter(HunJustLaunchedFsiSuppressor())
@@ -76,24 +168,6 @@ constructor(
started = true
}
- private class DecisionImpl(
- override val shouldInterrupt: Boolean,
- override val logReason: String
- ) : Decision
-
- private class FullScreenIntentDecisionImpl(
- override val shouldInterrupt: Boolean,
- override val wouldInterruptWithoutDnd: Boolean,
- override val logReason: String,
- val originalEntry: NotificationEntry,
- ) : FullScreenIntentDecision {
- var hasBeenLogged = false
- }
-
- private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
- private val conditions = mutableListOf<VisualInterruptionCondition>()
- private val filters = mutableListOf<VisualInterruptionFilter>()
-
override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
legacySuppressors.add(suppressor)
}
@@ -102,177 +176,144 @@ constructor(
legacySuppressors.remove(suppressor)
}
- fun addCondition(condition: VisualInterruptionCondition) {
+ override fun addCondition(condition: VisualInterruptionCondition) {
conditions.add(condition)
condition.start()
}
@VisibleForTesting
- fun removeCondition(condition: VisualInterruptionCondition) {
+ override fun removeCondition(condition: VisualInterruptionCondition) {
conditions.remove(condition)
}
- fun addFilter(filter: VisualInterruptionFilter) {
+ override fun addFilter(filter: VisualInterruptionFilter) {
filters.add(filter)
filter.start()
}
@VisibleForTesting
- fun removeFilter(filter: VisualInterruptionFilter) {
+ override fun removeFilter(filter: VisualInterruptionFilter) {
filters.remove(filter)
}
override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision {
check(started)
- return makeHeadsUpDecision(entry)
+
+ return if (statusBarStateController.isDozing) {
+ makeLoggablePulseDecision(entry)
+ } else {
+ makeLoggablePeekDecision(entry)
+ }
+ .decision
}
override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision {
check(started)
- return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) }
- }
- override fun makeUnloggedFullScreenIntentDecision(
- entry: NotificationEntry
- ): FullScreenIntentDecision {
- check(started)
- return makeFullScreenDecision(entry)
+ return if (statusBarStateController.isDozing) {
+ makeLoggablePulseDecision(entry).also { logDecision(PULSE, entry, it) }
+ } else {
+ makeLoggablePeekDecision(entry).also { logDecision(PEEK, entry, it) }
+ }
+ .decision
}
- override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
- check(started)
- val decisionImpl =
- decision as? FullScreenIntentDecisionImpl
- ?: run {
- Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision")
- return
- }
- if (decision.hasBeenLogged) {
- Log.wtf(TAG, "Already logged decision: $decision")
- return
- }
- logFullScreenIntentDecision(decisionImpl)
- decision.hasBeenLogged = true
- }
+ private fun makeLoggablePeekDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(PEEK)
+ ?: checkFilters(PEEK, entry) ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry) ?: checkSuppressAwakeHeadsUp(entry)
+ ?: LoggableDecision.unsuppressed
+
+ private fun makeLoggablePulseDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(PULSE)
+ ?: checkFilters(PULSE, entry) ?: checkSuppressInterruptions(entry)
+ ?: LoggableDecision.unsuppressed
override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision {
check(started)
- return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) }
- }
- private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl {
- if (statusBarStateController.isDozing) {
- return makePulseDecision(entry)
- } else {
- return makePeekDecision(entry)
- }
+ return makeLoggableBubbleDecision(entry).also { logDecision(BUBBLE, entry, it) }.decision
}
- private fun makePeekDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(PEEK)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(PEEK, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- checkAwakeSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeInterruptions"
- )
- }
- checkAwakeHeadsUpSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeHeadsUpInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
- }
+ private fun makeLoggableBubbleDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(BUBBLE)
+ ?: checkFilters(BUBBLE, entry) ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry) ?: LoggableDecision.unsuppressed
- private fun makePulseDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(PULSE)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(PULSE, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
+ private fun logDecision(
+ type: VisualInterruptionType,
+ entry: NotificationEntry,
+ loggableDecision: LoggableDecision
+ ) {
+ logger.logDecision(type.name, entry, loggableDecision.decision)
+ logEvents(entry, loggableDecision)
}
- private fun makeBubbleDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(BUBBLE)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(BUBBLE, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- checkAwakeSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
- }
+ override fun makeUnloggedFullScreenIntentDecision(
+ entry: NotificationEntry
+ ): FullScreenIntentDecision {
+ check(started)
- private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl {
- // Not yet implemented.
- return FullScreenIntentDecisionImpl(
- shouldInterrupt = true,
- wouldInterruptWithoutDnd = true,
- logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl",
- originalEntry = entry
- )
+ val couldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt
+ val fsiDecision =
+ fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, couldHeadsUp)
+ return FullScreenIntentDecisionImpl(entry, fsiDecision)
}
- private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) {
- // Not yet implemented.
- }
+ override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
+ check(started)
- private fun logBubbleDecision(entry: NotificationEntry, decision: DecisionImpl) {
- // Not yet implemented.
- }
+ if (decision !is FullScreenIntentDecisionImpl) {
+ Log.wtf(TAG, "FSI decision $decision was not created by this class")
+ return
+ }
- private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) {
- // Not yet implemented.
- }
+ if (decision.hasBeenLogged) {
+ Log.wtf(TAG, "FSI decision $decision has already been logged")
+ return
+ }
- private fun checkSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressInterruptions(entry) }
+ decision.hasBeenLogged = true
- private fun checkAwakeSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressAwakeInterruptions(entry) }
+ if (!decision.shouldLog) {
+ return
+ }
- private fun checkAwakeHeadsUpSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressAwakeHeadsUp(entry) }
+ logger.logFullScreenIntentDecision(decision.entry, decision, decision.isWarning)
+ logEvents(decision.entry, decision)
+ }
- private fun checkConditions(type: VisualInterruptionType): VisualInterruptionCondition? =
- conditions.firstOrNull { it.types.contains(type) && it.shouldSuppress() }
+ private fun logEvents(entry: NotificationEntry, loggable: Loggable) {
+ loggable.uiEventId?.let { uiEventLogger.log(it, entry.sbn.uid, entry.sbn.packageName) }
+ loggable.eventLogData?.let {
+ eventLog.writeEvent(0x534e4554, it.number, entry.sbn.uid, it.description)
+ }
+ }
- private fun checkFilters(
- type: VisualInterruptionType,
- entry: NotificationEntry
- ): VisualInterruptionFilter? =
- filters.firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) }
+ private fun checkSuppressInterruptions(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressInterruptions(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressInterruptions") }
+
+ private fun checkSuppressAwakeInterruptions(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressAwakeInterruptions(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressAwakeInterruptions") }
+
+ private fun checkSuppressAwakeHeadsUp(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressAwakeHeadsUp(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressAwakeHeadsUp") }
+
+ private fun checkConditions(type: VisualInterruptionType) =
+ conditions
+ .firstOrNull { it.types.contains(type) && it.shouldSuppress() }
+ ?.let { LoggableDecision.suppressed(it) }
+
+ private fun checkFilters(type: VisualInterruptionType, entry: NotificationEntry) =
+ filters
+ .firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) }
+ ?.let { LoggableDecision.suppressed(it) }
}
private const val TAG = "VisualInterruptionDecisionProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index 39199df37bd4..ee797274deac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData
/**
* A reason why visual interruptions might be suppressed.
@@ -43,6 +44,9 @@ enum class VisualInterruptionType {
* @see VisualInterruptionFilter
*/
sealed interface VisualInterruptionSuppressor {
+ /** Data to be logged in the EventLog when an interruption is suppressed. */
+ data class EventLogData(val number: String, val description: String)
+
/** The type(s) of interruption that this suppresses. */
val types: Set<VisualInterruptionType>
@@ -52,6 +56,9 @@ sealed interface VisualInterruptionSuppressor {
/** An optional UiEvent ID to be recorded when this suppresses an interruption. */
val uiEventId: UiEventEnum?
+ /** Optional data to be logged in the EventLog when this suppresses an interruption. */
+ val eventLogData: EventLogData?
+
/**
* Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before
* any other methods are called on the suppressor.
@@ -63,8 +70,14 @@ sealed interface VisualInterruptionSuppressor {
abstract class VisualInterruptionCondition(
override val types: Set<VisualInterruptionType>,
override val reason: String,
- override val uiEventId: UiEventEnum? = null
+ override val uiEventId: UiEventEnum? = null,
+ override val eventLogData: EventLogData? = null
) : VisualInterruptionSuppressor {
+ constructor(
+ types: Set<VisualInterruptionType>,
+ reason: String
+ ) : this(types, reason, /* uiEventId = */ null)
+
/** @return true if these interruptions should be suppressed right now. */
abstract fun shouldSuppress(): Boolean
}
@@ -73,8 +86,14 @@ abstract class VisualInterruptionCondition(
abstract class VisualInterruptionFilter(
override val types: Set<VisualInterruptionType>,
override val reason: String,
- override val uiEventId: UiEventEnum? = null
+ override val uiEventId: UiEventEnum? = null,
+ override val eventLogData: EventLogData? = null
) : VisualInterruptionSuppressor {
+ constructor(
+ types: Set<VisualInterruptionType>,
+ reason: String
+ ) : this(types, reason, /* uiEventId = */ null)
+
/**
* @param entry the notification to consider suppressing
* @return true if these interruptions should be suppressed for this notification right now
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 8f1e59d20091..7c8d76208e5e 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
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.util.DumpUtilsKt;
@@ -229,17 +230,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public void setBelowSpeedBump(boolean below) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
super.setBelowSpeedBump(below);
if (below != mIsBelowSpeedBump) {
mIsBelowSpeedBump = below;
updateBackgroundTint();
- onBelowSpeedBumpChanged();
}
}
- protected void onBelowSpeedBumpChanged() {
- }
-
/**
* Sets the tint color of the background
*/
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 11c65e542bcd..6cb079a22e7d 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
@@ -2221,8 +2221,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mMenuRow != null) {
mMenuRow.resetMenu();
}
- mTranslateAnim = null;
}
+ mTranslateAnim = null;
}
});
mTranslateAnim = translateAnim;
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 6edab4d26d59..49674d603509 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
@@ -38,6 +38,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.RoundableState;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.util.Compile;
@@ -400,6 +401,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
* @param below true if it is below.
*/
public void setBelowSpeedBump(boolean below) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
}
public int getPinnedHeadsUpHeight() {
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 b95e053ae508..8eda96f62257 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
@@ -156,7 +156,7 @@ public class NotificationBackgroundView extends View implements Dumpable {
new int[]{com.android.internal.R.attr.state_hovered},
new int[]{}},
- new int[]{0, 0, tintColor}
+ new int[]{tintColor, tintColor, tintColor}
);
mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
mBackground.setTintList(stateList);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index ec90a8d6ad59..162e8af47394 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -54,7 +54,7 @@ public abstract class StackScrollerDecorView extends ExpandableView {
mContent = findContentView();
mSecondaryView = findSecondaryView();
setVisible(false /* visible */, false /* animate */);
- setSecondaryVisible(false /* visible */, false /* animate */);
+ setSecondaryVisible(false /* visible */, false /* animate */, null /* onAnimationEnd */);
setOutlineProvider(null);
}
@@ -155,15 +155,23 @@ public abstract class StackScrollerDecorView extends ExpandableView {
/**
* Set the secondary view of this layout to visible.
*
- * @param visible should the secondary view be visible
- * @param animate should the change be animated
+ * @param visible True if the contents should be visible.
+ * @param animate True if we should fade to new visibility.
+ * @param onAnimationEnded Callback to run after visibility updates, takes a boolean as a
+ * parameter that represents whether the animation was cancelled.
*/
- protected void setSecondaryVisible(boolean visible, boolean animate) {
+ protected void setSecondaryVisible(boolean visible, boolean animate,
+ Consumer<Boolean> onAnimationEnded) {
if (mIsSecondaryVisible != visible) {
mSecondaryAnimating = animate;
mIsSecondaryVisible = visible;
- setViewVisible(mSecondaryView, visible, animate,
- (cancelled) -> onSecondaryVisibilityAnimationEnd());
+ Consumer<Boolean> onAnimationEndedWrapper = (cancelled) -> {
+ onContentVisibilityAnimationEnd();
+ if (onAnimationEnded != null) {
+ onAnimationEnded.accept(cancelled);
+ }
+ };
+ setViewVisible(mSecondaryView, visible, animate, onAnimationEndedWrapper);
}
if (!mSecondaryAnimating) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 7667e17cac14..5cdead407891 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -24,6 +24,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
@@ -40,6 +41,7 @@ object NotificationShelfViewBinder {
configuration: ConfigurationState,
configurationController: ConfigurationController,
falsingManager: FalsingManager,
+ iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
notificationIconAreaController: NotificationIconAreaController,
shelfIconViewStore: ShelfNotificationIconViewStore,
) {
@@ -51,6 +53,7 @@ object NotificationShelfViewBinder {
viewModel.icons,
configuration,
configurationController,
+ iconViewBindingFailureTracker,
shelfIconViewStore,
)
} else {
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 cb671447bbb4..d9c51089d5f8 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
@@ -121,6 +121,7 @@ public class AmbientState implements Dumpable {
private float mAppearFraction;
private float mOverExpansion;
private int mStackTopMargin;
+ private boolean mUseSplitShade;
/** Distance of top of notifications panel from top of screen. */
private float mStackY = 0;
@@ -228,6 +229,20 @@ public class AmbientState implements Dumpable {
}
/**
+ * @param useSplitShade True if we are showing split shade.
+ */
+ public void setUseSplitShade(boolean useSplitShade) {
+ mUseSplitShade = useSplitShade;
+ }
+
+ /**
+ * @return True if we are showing split shade.
+ */
+ public boolean getUseSplitShade() {
+ return mUseSplitShade;
+ }
+
+ /**
* @return Fraction of shade expansion.
*/
public float getExpansionFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index 33473a6f6b0a..d0c5c82b50ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -26,6 +26,7 @@ import com.android.app.animation.Interpolators;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
/**
* A state of an expandable view
@@ -162,7 +163,9 @@ public class ExpandableViewState extends ViewState {
this.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
// apply below shelf speed bump
- expandableView.setBelowSpeedBump(this.belowSpeedBump);
+ if (!NotificationIconContainerRefactor.isEnabled()) {
+ expandableView.setBelowSpeedBump(this.belowSpeedBump);
+ }
// apply clipping
final float oldClipTopAmount = expandableView.getClipTopAmount();
@@ -217,7 +220,9 @@ public class ExpandableViewState extends ViewState {
expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed);
// apply below the speed bump
- expandableView.setBelowSpeedBump(this.belowSpeedBump);
+ if (!NotificationIconContainerRefactor.isEnabled()) {
+ expandableView.setBelowSpeedBump(this.belowSpeedBump);
+ }
// start hiding sensitive animation
expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive,
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 a0ad560e817e..283a5930f930 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
@@ -20,6 +20,7 @@ import static android.os.Trace.TRACE_TAG_APP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
+import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
@@ -202,9 +203,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
private final RefactorFlag mAnimatedInsets;
-
- private final boolean mNewAodTransition;
-
private int mContentHeight;
private float mIntrinsicContentHeight;
private int mPaddingBetweenElements;
@@ -253,6 +251,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
+ private Runnable mOnHeightChangedRunnable;
private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
private boolean mNeedsAnimation;
private boolean mTopPaddingNeedsAnimation;
@@ -632,7 +631,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mIsSmallLandscapeLockscreenEnabled = mFeatureFlags.isEnabled(
Flags.LOCKSCREEN_ENABLE_LANDSCAPE);
mDebugLines = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
- mNewAodTransition = mFeatureFlags.isEnabled(Flags.NEW_AOD_TRANSITION);
mDebugRemoveAnimation = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
mAnimatedInsets =
@@ -742,7 +740,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateFooter();
}
- void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
+ /** Setter for filtered notifs, to be removed with the FooterViewRefactor flag. */
+ public void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
+ FooterViewRefactor.assertInLegacyMode();
mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
}
@@ -751,7 +751,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mFooterView == null || mController == null) {
return;
}
- // TODO: move this logic to controller, which will invoke updateFooterView directly
final boolean showHistory = mController.isHistoryEnabled();
final boolean showDismissView = shouldShowDismissView();
@@ -775,13 +774,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
&& !mIsRemoteInputActive;
}
- /**
- * Return whether there are any clearable notifications
- */
- boolean hasActiveClearableNotifications(@SelectedRows int selection) {
- return mController.hasActiveClearableNotifications(selection);
- }
-
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@@ -1121,6 +1113,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
}
+
+ if (mOnHeightChangedRunnable != null) {
+ mOnHeightChangedRunnable.run();
+ }
}
public boolean isPulseExpanding() {
@@ -1157,6 +1153,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mSpeedBumpIndex;
}
+ private boolean mSuppressChildrenMeasureAndLayout = false;
+
+ /**
+ * Similar to {@link ViewGroup#suppressLayout} but still performs layout of
+ * the container itself and suppresses only measure and layout calls to children.
+ */
+ public void suppressChildrenMeasureAndLayout(boolean suppress) {
+ mSuppressChildrenMeasureAndLayout = suppress;
+
+ if (!suppress) {
+ requestLayout();
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection("NotificationStackScrollLayout#onMeasure");
@@ -1169,6 +1179,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
int width = MeasureSpec.getSize(widthMeasureSpec);
updateSidePadding(width);
+
+ if (mSuppressChildrenMeasureAndLayout) {
+ Trace.endSection();
+ return;
+ }
+
int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
MeasureSpec.getMode(widthMeasureSpec));
// Don't constrain the height of the children so we know how big they'd like to be
@@ -1192,18 +1208,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // we layout all our children centered on the top
- float centerX = getWidth() / 2.0f;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- // We need to layout all children even the GONE ones, such that the heights are
- // calculated correctly as they are used to calculate how many we can fit on the screen
- float width = child.getMeasuredWidth();
- float height = child.getMeasuredHeight();
- child.layout((int) (centerX - width / 2.0f),
- 0,
- (int) (centerX + width / 2.0f),
- (int) height);
+ if (!mSuppressChildrenMeasureAndLayout) {
+ // we layout all our children centered on the top
+ float centerX = getWidth() / 2.0f;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ // We need to layout all children even the GONE ones, such that the heights are
+ // calculated correctly as they are used to calculate how many we can fit on
+ // the screen
+ float width = child.getMeasuredWidth();
+ float height = child.getMeasuredHeight();
+ child.layout((int) (centerX - width / 2.0f),
+ 0,
+ (int) (centerX + width / 2.0f),
+ (int) height);
+ }
}
setMaxLayoutHeight(getHeight());
updateContentHeight();
@@ -1434,7 +1453,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@VisibleForTesting
public void updateStackHeight(float endHeight, float fraction) {
- if (!mNewAodTransition) {
+ if (!newAodTransition()) {
// During the (AOD<=>LS) transition where dozeAmount is changing,
// apply dozeAmount to stack height instead of expansionFraction
// to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
@@ -1633,8 +1652,44 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
+ *
+ * TODO(b/308591475): This entire logic can probably be improved as part of the empty shade
+ * refactor, but for now:
+ * - if the empty shade is visible, we can assume that the visible notif count is not 0;
+ * - if the shelf is visible, we can assume there are at least 2 notifications present (this
+ * is not true for AOD, but this logic refers to the expansion of the shade, where we never
+ * have the shelf on its own)
*/
private float getAppearEndPosition() {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ return getAppearEndPositionLegacy();
+ }
+
+ int appearPosition = mAmbientState.getStackTopMargin();
+ if (mEmptyShadeView.getVisibility() == GONE) {
+ if (isHeadsUpTransition()
+ || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
+ if (mShelf.getVisibility() != GONE) {
+ appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
+ }
+ appearPosition += getTopHeadsUpPinnedHeight()
+ + getPositionInLinearLayout(mAmbientState.getTrackedHeadsUpRow());
+ } else if (mShelf.getVisibility() != GONE) {
+ appearPosition += mShelf.getIntrinsicHeight();
+ }
+ } else {
+ appearPosition = mEmptyShadeView.getHeight();
+ }
+ return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
+ }
+
+ /**
+ * The version of {@code getAppearEndPosition} that uses the notif count. The view shouldn't
+ * need to know about that, so we want to phase this out with the footer view refactor.
+ */
+ private float getAppearEndPositionLegacy() {
+ FooterViewRefactor.assertInLegacyMode();
+
int appearPosition = mAmbientState.getStackTopMargin();
int visibleNotifCount = mController.getVisibleNotificationCount();
if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
@@ -1673,7 +1728,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// This can't use expansion fraction as that goes only from 0 to 1. Also when
// appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3
// and that makes translation jump immediately.
- float appearEndPosition = getAppearEndPosition();
+ float appearEndPosition = FooterViewRefactor.isEnabled() ? getAppearEndPosition()
+ : getAppearEndPositionLegacy();
float appearStartPosition = getAppearStartPosition();
float hunAppearFraction = (height - appearStartPosition)
/ (appearEndPosition - appearStartPosition);
@@ -4229,6 +4285,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
this.mOnHeightChangedListener = onHeightChangedListener;
}
+ void setOnHeightChangedRunnable(Runnable r) {
+ this.mOnHeightChangedRunnable = r;
+ }
+
void onChildAnimationFinished() {
setAnimationRunning(false);
requestChildrenUpdate();
@@ -4548,13 +4608,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mManageButtonClickListener != null) {
mFooterView.setManageButtonClickListener(mManageButtonClickListener);
}
- mFooterView.setClearAllButtonClickListener(v -> {
- if (mFooterClearAllListener != null) {
- mFooterClearAllListener.onClearAll();
- }
- clearNotifications(ROWS_ALL, true /* closeShade */);
- footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
- });
+ if (!FooterViewRefactor.isEnabled()) {
+ mFooterView.setClearAllButtonClickListener(v -> {
+ if (mFooterClearAllListener != null) {
+ mFooterClearAllListener.onClearAll();
+ }
+ clearNotifications(ROWS_ALL, true /* closeShade */);
+ footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
+ });
+ }
if (FooterViewRefactor.isEnabled()) {
updateFooter();
}
@@ -4570,12 +4632,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
addView(mEmptyShadeView, index);
}
- void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
+ /** Legacy version, should be removed with the footer refactor flag. */
+ public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
+ FooterViewRefactor.assertInLegacyMode();
+ updateEmptyShadeView(visible, areNotificationsHiddenInShade,
+ mHasFilteredOutSeenNotifications);
+ }
+
+ /** Trigger an update for the empty shade resources and visibility. */
+ public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade,
+ boolean hasFilteredOutSeenNotifications) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
if (areNotificationsHiddenInShade) {
updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
- } else if (mHasFilteredOutSeenNotifications) {
+ } else if (hasFilteredOutSeenNotifications) {
updateEmptyShadeView(
R.string.no_unseen_notif_text,
R.string.unlock_to_see_notif_text,
@@ -4618,9 +4689,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
boolean animate = mIsExpanded && mAnimationsEnabled;
mFooterView.setVisible(visible, animate);
- mFooterView.setClearAllButtonVisible(showDismissView, animate);
mFooterView.showHistory(showHistory);
if (!FooterViewRefactor.isEnabled()) {
+ mFooterView.setClearAllButtonVisible(showDismissView, animate);
mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
}
}
@@ -4636,22 +4707,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mClearAllInProgress;
}
- public boolean isFooterViewNotGone() {
- return mFooterView != null
- && mFooterView.getVisibility() != View.GONE
- && !mFooterView.willBeGone();
- }
-
- public boolean isFooterViewContentVisible() {
- return mFooterView != null && mFooterView.isContentVisible();
- }
-
- public int getFooterViewHeightWithPadding() {
- return mFooterView == null ? 0 : mFooterView.getHeight()
- + mPaddingBetweenElements
- + mGapHeight;
- }
-
/**
* @return the padding after the media header on the lockscreen
*/
@@ -4768,10 +4823,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void removeContainerView(View v) {
Assert.isMainThread();
removeView(v);
- if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) {
- mController.updateShowEmptyShadeView();
- updateFooter();
- mController.updateImportantForAccessibility();
+ if (!FooterViewRefactor.isEnabled()) {
+ // A notification was removed, and we're not currently showing the empty shade view.
+ if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) {
+ mController.updateShowEmptyShadeView();
+ updateFooter();
+ mController.updateImportantForAccessibility();
+ }
}
updateSpeedBumpIndex();
@@ -4780,10 +4838,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void addContainerView(View v) {
Assert.isMainThread();
addView(v);
- if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
- mController.updateShowEmptyShadeView();
- updateFooter();
- mController.updateImportantForAccessibility();
+ if (!FooterViewRefactor.isEnabled()) {
+ // A notification was added, and we're currently showing the empty shade view.
+ if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
+ mController.updateShowEmptyShadeView();
+ updateFooter();
+ mController.updateImportantForAccessibility();
+ }
}
updateSpeedBumpIndex();
@@ -4793,7 +4854,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
Assert.isMainThread();
ensureRemovedFromTransientContainer(v);
addView(v, index);
- if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
+ // A notification was added, and we're currently showing the empty shade view.
+ if (!FooterViewRefactor.isEnabled() && v instanceof ExpandableNotificationRow
+ && mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
updateFooter();
mController.updateImportantForAccessibility();
@@ -5066,7 +5129,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mEmptyShadeView.getVisibility() == GONE) {
return getMinExpansionHeight();
} else {
- return getAppearEndPosition();
+ return FooterViewRefactor.isEnabled() ? getAppearEndPosition()
+ : getAppearEndPositionLegacy();
}
}
@@ -5097,6 +5161,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
println(pw, "qsClipDismiss", mDismissUsingRowTranslationX);
println(pw, "visibility", visibilityString(getVisibility()));
println(pw, "alpha", getAlpha());
+ println(pw, "suppressChildrenMeasureLayout", mSuppressChildrenMeasureAndLayout);
println(pw, "scrollY", mAmbientState.getScrollY());
println(pw, "maxTopPadding", mMaxTopPadding);
println(pw, "showShelfOnly", mShouldShowShelfOnly);
@@ -5292,11 +5357,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return viewsToRemove;
}
+ /** Clear all clearable notifications when the user requests it. */
+ public void clearAllNotifications() {
+ clearNotifications(ROWS_ALL, /* closeShade = */ true);
+ }
+
/**
* Collects a list of visible rows, and animates them away in a staggered fashion as if they
* were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
*/
- @VisibleForTesting
void clearNotifications(@SelectedRows int selection, boolean closeShade) {
// Animate-swipe all dismissable notifications, then animate the shade closed
final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
@@ -5596,6 +5665,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void setFooterClearAllListener(FooterClearAllListener listener) {
+ FooterViewRefactor.assertInLegacyMode();
mFooterClearAllListener = listener;
}
@@ -5666,6 +5736,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean split = mSplitShadeStateController.shouldUseSplitNotificationShade(getResources());
if (split != mShouldUseSplitNotificationShade) {
mShouldUseSplitNotificationShade = split;
+ mAmbientState.setUseSplitShade(split);
updateDismissBehavior();
updateUseRoundedRectClipping();
}
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 44140b926011..e6315fd159d5 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
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.stack;
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import static com.android.app.animation.Interpolators.STANDARD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -27,10 +29,11 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.animation.ObjectAnimator;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Point;
import android.os.Trace;
import android.os.UserHandle;
@@ -38,6 +41,7 @@ import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.Pair;
+import android.util.Property;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
@@ -53,14 +57,14 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.view.OneShotPreDrawListener;
+import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
@@ -87,7 +91,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -104,8 +107,9 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -115,11 +119,9 @@ import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -133,6 +135,7 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Compile;
import com.android.systemui.util.settings.SecureSettings;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
@@ -145,7 +148,7 @@ import javax.inject.Named;
* Controller for {@link NotificationStackScrollLayout}.
*/
@SysUISingleton
-public class NotificationStackScrollLayoutController {
+public class NotificationStackScrollLayoutController implements Dumpable {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final String HIGH_PRIORITY = "high_priority";
@@ -190,7 +193,6 @@ public class NotificationStackScrollLayoutController {
private final GroupExpansionManager mGroupExpansionManager;
private final SeenNotificationsInteractor mSeenNotificationsInteractor;
private final KeyguardTransitionRepository mKeyguardTransitionRepo;
-
private NotificationStackScrollLayout mView;
private NotificationSwipeHelper mSwipeHelper;
@Nullable
@@ -222,7 +224,9 @@ public class NotificationStackScrollLayoutController {
@Override
public void onViewAttachedToWindow(View v) {
mConfigurationController.addCallback(mConfigurationListener);
- mZenModeController.addCallback(mZenModeControllerCallback);
+ if (!FooterViewRefactor.isEnabled()) {
+ mZenModeController.addCallback(mZenModeControllerCallback);
+ }
final int newBarState = mStatusBarStateController.getState();
if (newBarState != mBarState) {
mStateListener.onStateChanged(newBarState);
@@ -235,11 +239,29 @@ public class NotificationStackScrollLayoutController {
@Override
public void onViewDetachedFromWindow(View v) {
mConfigurationController.removeCallback(mConfigurationListener);
- mZenModeController.removeCallback(mZenModeControllerCallback);
+ if (!FooterViewRefactor.isEnabled()) {
+ mZenModeController.removeCallback(mZenModeControllerCallback);
+ }
mStatusBarStateController.removeCallback(mStateListener);
}
};
+ private static final Property<NotificationStackScrollLayoutController, Float>
+ HIDE_ALPHA_PROPERTY = new Property<>(Float.class, "HideNotificationsAlpha") {
+ @Override
+ public Float get(NotificationStackScrollLayoutController object) {
+ return object.mMaxAlphaForUnhide;
+ }
+
+ @Override
+ public void set(NotificationStackScrollLayoutController object, Float value) {
+ object.setMaxAlphaForUnhide(value);
+ }
+ };
+
+ @Nullable
+ private ObjectAnimator mHideAlphaAnimator = null;
+
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@Override
@@ -275,7 +297,9 @@ public class NotificationStackScrollLayoutController {
final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onDensityOrFontScaleChanged() {
- updateShowEmptyShadeView();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateShowEmptyShadeView();
+ }
mView.reinflateViews();
}
@@ -291,7 +315,9 @@ public class NotificationStackScrollLayoutController {
mView.updateBgColor();
mView.updateDecorViews();
mView.reinflateViews();
- updateShowEmptyShadeView();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateShowEmptyShadeView();
+ }
updateFooter();
}
@@ -302,6 +328,8 @@ public class NotificationStackScrollLayoutController {
};
private NotifStats mNotifStats = NotifStats.getEmpty();
+ private float mMaxAlphaForExpansion = 1.0f;
+ private float mMaxAlphaForUnhide = 1.0f;
private final NotificationListViewBinder mViewBinder;
@@ -338,7 +366,9 @@ public class NotificationStackScrollLayoutController {
mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
mLockscreenUserManager.isAnyProfilePublicMode());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
- updateImportantForAccessibility();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateImportantForAccessibility();
+ }
}
};
@@ -440,7 +470,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onSnooze(StatusBarNotification sbn,
- NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
+ NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
@@ -562,7 +592,7 @@ public class NotificationStackScrollLayoutController {
@Override
public boolean updateSwipeProgress(View animView, boolean dismissable,
- float swipeProgress) {
+ float swipeProgress) {
// Returning true prevents alpha fading.
return false;
}
@@ -654,6 +684,7 @@ public class NotificationStackScrollLayoutController {
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
+ ActiveNotificationsInteractor activeNotificationsInteractor,
SeenNotificationsInteractor seenNotificationsInteractor,
NotificationListViewBinder viewBinder,
ShadeController shadeController,
@@ -713,6 +744,7 @@ public class NotificationStackScrollLayoutController {
mDismissibilityProvider = dismissibilityProvider;
mActivityStarter = activityStarter;
mView.passSplitShadeStateController(splitShadeStateController);
+ mDumpManager.registerDumpable(this);
updateResources();
setUpView();
}
@@ -727,8 +759,10 @@ public class NotificationStackScrollLayoutController {
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
- mView.setFooterClearAllListener(() ->
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ if (!FooterViewRefactor.isEnabled()) {
+ mView.setFooterClearAllListener(() ->
+ mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ }
mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
@Override
@@ -818,10 +852,12 @@ public class NotificationStackScrollLayoutController {
mGroupExpansionManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
- mViewBinder.bind(mView);
+ mViewBinder.bind(mView, this);
- collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
- this::onKeyguardTransitionChanged);
+ if (!FooterViewRefactor.isEnabled()) {
+ collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
+ this::onKeyguardTransitionChanged);
+ }
}
private boolean isInVisibleLocation(NotificationEntry entry) {
@@ -838,10 +874,6 @@ public class NotificationStackScrollLayoutController {
return row.getVisibility() == View.VISIBLE;
}
- public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- return mNotificationRoundnessManager.isViewAffectedBySwipe(expandableView);
- }
-
public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mView.addOnExpandedHeightChangedListener(listener);
}
@@ -875,6 +907,10 @@ public class NotificationStackScrollLayoutController {
mView.requestLayout();
}
+ public void addOneShotPreDrawListener(Runnable runnable) {
+ OneShotPreDrawListener.add(mView, runnable);
+ }
+
public Display getDisplay() {
return mView.getDisplay();
}
@@ -930,6 +966,13 @@ public class NotificationStackScrollLayoutController {
mView.setOnHeightChangedListener(listener);
}
+ /**
+ * Invoked in addition to {@see #setOnHeightChangedListener}
+ */
+ public void setOnHeightChangedRunnable(Runnable r) {
+ mView.setOnHeightChangedRunnable(r);
+ }
+
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener listener) {
mView.setOverscrollTopChangedListener(listener);
@@ -1004,6 +1047,8 @@ public class NotificationStackScrollLayoutController {
}
public int getVisibleNotificationCount() {
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle footer
+ // visibility in the refactored code
return mNotifStats.getNumActiveNotifs();
}
@@ -1047,7 +1092,7 @@ public class NotificationStackScrollLayoutController {
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
- boolean cancelAnimators) {
+ boolean cancelAnimators) {
mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
}
@@ -1097,6 +1142,7 @@ public class NotificationStackScrollLayoutController {
}
public void setQsFullScreen(boolean fullScreen) {
+ FooterViewRefactor.assertInLegacyMode();
mView.setQsFullScreen(fullScreen);
updateShowEmptyShadeView();
}
@@ -1157,12 +1203,49 @@ public class NotificationStackScrollLayoutController {
return mView.getEmptyShadeViewHeight();
}
- public void setAlpha(float alpha) {
+ public void setMaxAlphaForExpansion(float alpha) {
+ mMaxAlphaForExpansion = alpha;
+ updateAlpha();
+ }
+
+ private void setMaxAlphaForUnhide(float alpha) {
+ mMaxAlphaForUnhide = alpha;
+ updateAlpha();
+ }
+
+ private void updateAlpha() {
if (mView != null) {
- mView.setAlpha(alpha);
+ mView.setAlpha(Math.min(mMaxAlphaForExpansion, mMaxAlphaForUnhide));
+ }
+ }
+
+ public void setSuppressChildrenMeasureAndLayout(boolean suppressLayout) {
+ mView.suppressChildrenMeasureAndLayout(suppressLayout);
+ }
+
+ public void updateNotificationsContainerVisibility(boolean visible, boolean animate) {
+ if (mHideAlphaAnimator != null) {
+ mHideAlphaAnimator.cancel();
+ }
+
+ final float targetAlpha = visible ? 1f : 0f;
+
+ if (animate) {
+ mHideAlphaAnimator = createAlphaAnimator(targetAlpha);
+ mHideAlphaAnimator.start();
+ } else {
+ HIDE_ALPHA_PROPERTY.set(this, targetAlpha);
}
}
+ private ObjectAnimator createAlphaAnimator(float targetAlpha) {
+ final ObjectAnimator objectAnimator = ObjectAnimator
+ .ofFloat(this, HIDE_ALPHA_PROPERTY, targetAlpha);
+ objectAnimator.setInterpolator(STANDARD);
+ objectAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+ return objectAnimator;
+ }
+
public float calculateAppearFraction(float height) {
return mView.calculateAppearFraction(height);
}
@@ -1193,18 +1276,6 @@ public class NotificationStackScrollLayoutController {
mView.setPanelFlinging(flinging);
}
- public boolean isFooterViewNotGone() {
- return mView.isFooterViewNotGone();
- }
-
- public boolean isFooterViewContentVisible() {
- return mView.isFooterViewContentVisible();
- }
-
- public int getFooterViewHeightWithPadding() {
- return mView.getFooterViewHeightWithPadding();
- }
-
/**
* Sets whether the bouncer is currently showing. Should only be called from
* {@link CentralSurfaces}.
@@ -1221,7 +1292,10 @@ public class NotificationStackScrollLayoutController {
public void updateVisibility(boolean visible) {
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- if (mView.getVisibility() == View.VISIBLE) {
+ // Refactor note: the empty shade's visibility doesn't seem to actually depend on the
+ // parent visibility (so this update seemingly doesn't do anything). Therefore, this is not
+ // modeled in the refactored code.
+ if (!FooterViewRefactor.isEnabled() && mView.getVisibility() == View.VISIBLE) {
// Synchronize EmptyShadeView visibility with the parent container.
updateShowEmptyShadeView();
updateImportantForAccessibility();
@@ -1236,6 +1310,8 @@ public class NotificationStackScrollLayoutController {
* are true.
*/
public void updateShowEmptyShadeView() {
+ FooterViewRefactor.assertInLegacyMode();
+
Trace.beginSection("NSSLC.updateShowEmptyShadeView");
final boolean shouldShow = getVisibleNotificationCount() == 0
@@ -1279,6 +1355,7 @@ public class NotificationStackScrollLayoutController {
* auto-scrolling in NSSL.
*/
public void updateImportantForAccessibility() {
+ FooterViewRefactor.assertInLegacyMode();
if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) {
mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
} else {
@@ -1286,16 +1363,6 @@ public class NotificationStackScrollLayoutController {
}
}
- /**
- * @return true if {@link StatusBarStateController} is in transition to the KEYGUARD
- * and false otherwise.
- */
- private boolean isInTransitionToKeyguard() {
- final int currentState = mStatusBarStateController.getState();
- final int upcomingState = mStatusBarStateController.getCurrentOrUpcomingState();
- return (currentState != upcomingState && upcomingState == KEYGUARD);
- }
-
public boolean isShowingEmptyShadeView() {
return mView.isEmptyShadeViewVisible();
}
@@ -1343,10 +1410,14 @@ public class NotificationStackScrollLayoutController {
* Return whether there are any clearable notifications
*/
public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
+ // visibility in the refactored code
return hasNotifications(selection, true /* clearable */);
}
public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
+ // visibility in the refactored code
boolean hasAlertingMatchingClearable = isClearable
? mNotifStats.getHasClearableAlertingNotifs()
: mNotifStats.getHasNonClearableAlertingNotifs();
@@ -1385,7 +1456,7 @@ public class NotificationStackScrollLayoutController {
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
- boolean remoteInputActive) {
+ boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
@@ -1504,7 +1575,7 @@ public class NotificationStackScrollLayoutController {
}
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
- @SelectedRows int selectedRows) {
+ @SelectedRows int selectedRows) {
if (selectedRows == ROWS_ALL) {
mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId());
@@ -1596,7 +1667,7 @@ public class NotificationStackScrollLayoutController {
* Set rounded rect clipping bounds on this view.
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
- int bottomRadius) {
+ int bottomRadius) {
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
}
@@ -1626,6 +1697,7 @@ public class NotificationStackScrollLayoutController {
@VisibleForTesting
void onKeyguardTransitionChanged(TransitionStep transitionStep) {
+ FooterViewRefactor.assertInLegacyMode();
boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD)
&& (transitionStep.getFrom().equals(KeyguardState.GONE)
|| transitionStep.getFrom().equals(KeyguardState.OCCLUDED));
@@ -1635,6 +1707,12 @@ public class NotificationStackScrollLayoutController {
}
}
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mMaxAlphaForExpansion=" + mMaxAlphaForExpansion);
+ pw.println("mMaxAlphaForUnhide=" + mMaxAlphaForUnhide);
+ }
+
/**
* Enum for UiEvent logged from this class
*/
@@ -1945,11 +2023,21 @@ public class NotificationStackScrollLayoutController {
private class NotifStackControllerImpl implements NotifStackController {
@Override
public void setNotifStats(@NonNull NotifStats notifStats) {
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once footer visibility
+ // is handled in the refactored stack.
mNotifStats = notifStats;
- mView.setHasFilteredOutSeenNotifications(
- mSeenNotificationsInteractor.getHasFilteredOutSeenNotifications().getValue());
+
+ if (!FooterViewRefactor.isEnabled()) {
+ mView.setHasFilteredOutSeenNotifications(
+ mSeenNotificationsInteractor
+ .getHasFilteredOutSeenNotifications().getValue());
+ }
+
updateFooter();
- updateShowEmptyShadeView();
+
+ if (!FooterViewRefactor.isEnabled()) {
+ updateShowEmptyShadeView();
+ }
}
}
}
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 80f98a68b4f4..06ca9a50bb6d 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
@@ -572,7 +572,8 @@ public class StackScrollAlgorithm {
float viewEnd = viewState.getYTranslation() + viewState.height + ambientState.getStackY();
maybeUpdateHeadsUpIsVisible(viewState, ambientState.isShadeExpanded(),
- view.mustStayOnScreen(), /* topVisible */ viewState.getYTranslation() >= 0,
+ view.mustStayOnScreen(),
+ /* topVisible= */ viewState.getYTranslation() >= mNotificationScrimPadding,
viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation()
);
if (view instanceof FooterView) {
@@ -777,8 +778,12 @@ public class StackScrollAlgorithm {
if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
childState.headsUpIsVisible, row.showingPulsing(),
ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
- // Ensure that the heads up is always visible even when scrolled off
- clampHunToTop(mQuickQsOffsetHeight, ambientState.getStackTranslation(),
+ // Ensure that the heads up is always visible even when scrolled off.
+ // NSSL y starts at top of screen in non-split-shade, but below the qs offset
+ // in split shade, so we only need to inset by the scrim padding in split shade.
+ final float clampInset = ambientState.getUseSplitShade()
+ ? mNotificationScrimPadding : mQuickQsOffsetHeight;
+ clampHunToTop(clampInset, ambientState.getStackTranslation(),
row.getCollapsedHeight(), childState);
if (isTopEntry && row.isAboveShelf()) {
// the first hun can't get off screen.
@@ -838,10 +843,10 @@ public class StackScrollAlgorithm {
* Transition pinned collapsed HUN to full height when scrolling back up.
*/
@VisibleForTesting
- void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight,
+ void clampHunToTop(float clampInset, float stackTranslation, float collapsedHeight,
ExpandableViewState viewState) {
- final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation,
+ final float newTranslation = Math.max(clampInset + stackTranslation,
viewState.getYTranslation());
// Transition from collapsed pinned state to fully expanded state
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
new file mode 100644
index 000000000000..7c10663bbb50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.stack.data.repository
+
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** A repository which holds state about and controlling the appearance of the NSSL */
+@SysUISingleton
+class NotificationStackAppearanceRepository @Inject constructor() {
+ /** The position of the notification stack in the current scene */
+ val stackPosition = MutableStateFlow(SharedNotificationContainerPosition(0f, 0f))
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
new file mode 100644
index 000000000000..4de3a7fd9ff1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.stack.domain.interactor
+
+import android.graphics.Rect
+import android.util.Log
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.systemui.common.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.kotlin.WithPrev
+import com.android.systemui.util.kotlin.area
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.race
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withTimeout
+import javax.inject.Inject
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class HideNotificationsInteractor
+@Inject
+constructor(
+ private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
+ private val configurationInteractor: ConfigurationInteractor,
+ private val animationsStatus: AnimationStatusRepository,
+ private val powerInteractor: PowerInteractor
+) {
+
+ val shouldHideNotifications: Flow<Boolean>
+ get() =
+ if (!unfoldTransitionInteractor.isAvailable) {
+ // Do nothing on non-foldable devices
+ emptyFlow()
+ } else {
+ screenSizeChangesFlow
+ .flatMapLatest {
+ flow {
+ // Hide notifications on each display resize
+ emit(true)
+ try {
+ waitForDisplaySwitchFinish(it)
+ } catch (_: TimeoutCancellationException) {
+ Log.e(TAG, "Timed out waiting for display switch")
+ } finally {
+ emit(false)
+ }
+ }
+ }
+ .distinctUntilChanged()
+ .traceEach(HIDE_STATUS_TRACK_NAME, logcat = true) { shouldHide ->
+ if (shouldHide) "hidden" else "visible"
+ }
+ }
+
+ private suspend fun waitForDisplaySwitchFinish(screenSizeChange: WithPrev<Rect, Rect>) {
+ withTimeout(timeMillis = DISPLAY_SWITCH_TIMEOUT_MILLIS) {
+ val waitForDisplaySwitchOrAnimation: suspend () -> Unit = {
+ if (shouldWaitForAnimationEnd(screenSizeChange)) {
+ unfoldTransitionInteractor.waitForTransitionFinish()
+ } else {
+ waitForScreenTurnedOn()
+ }
+ }
+
+ race({ waitForDisplaySwitchOrAnimation() }, { waitForGoingToSleep() })
+ }
+ }
+
+ private suspend fun shouldWaitForAnimationEnd(screenSizeChange: WithPrev<Rect, Rect>): Boolean =
+ animationsStatus.areAnimationsEnabled().first() && screenSizeChange.isUnfold
+
+ private suspend fun waitForScreenTurnedOn() =
+ powerInteractor.screenPowerState.filter { it == SCREEN_ON }.first()
+
+ private suspend fun waitForGoingToSleep() =
+ powerInteractor.detailedWakefulness.filter { it.isAsleep() }.first()
+
+ private val screenSizeChangesFlow: Flow<WithPrev<Rect, Rect>>
+ get() = configurationInteractor.naturalMaxBounds.pairwise()
+
+ private val WithPrev<Rect, Rect>.isUnfold: Boolean
+ get() = newValue.area > previousValue.area
+
+ private companion object {
+ private const val TAG = "DisplaySwitchNotificationsHideInteractor"
+ private const val HIDE_STATUS_TRACK_NAME = "NotificationsHiddenForDisplayChange"
+ private const val DISPLAY_SWITCH_TIMEOUT_MILLIS = 5_000L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
new file mode 100644
index 000000000000..820fe0b1f11e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.stack.domain.interactor
+
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** An interactor which controls the appearance of the NSSL */
+@SysUISingleton
+class NotificationStackAppearanceInteractor
+@Inject
+constructor(
+ private val repository: NotificationStackAppearanceRepository,
+) {
+ /** The position of the notification stack in the current scene */
+ val stackPosition: StateFlow<SharedNotificationContainerPosition>
+ get() = repository.stackPosition.asStateFlow()
+
+ /** Sets the position of the notification stack in the current scene */
+ fun setStackPosition(position: SharedNotificationContainerPosition) {
+ check(position.top <= position.bottom) { "Invalid position: $position" }
+ repository.stackPosition.value = position
+ }
+}
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 57cea5d3b31e..c2c5eed6f013 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,13 +18,15 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -43,6 +45,10 @@ constructor(
private val _topPosition = MutableStateFlow(0f)
val topPosition = _topPosition.asStateFlow()
+ private val _notificationStackChanged = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+ /** An internal modification was made to notifications */
+ val notificationStackChanged = _notificationStackChanged.asSharedFlow()
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
configurationRepository.onAnyConfigurationChange
.onStart { emit(Unit) }
@@ -67,11 +73,21 @@ constructor(
}
.distinctUntilChanged()
+ val isSplitShadeEnabled: Flow<Boolean> =
+ configurationBasedDimensions
+ .map { dimens: ConfigurationBasedDimensions -> dimens.useSplitShade }
+ .distinctUntilChanged()
+
/** Top position (without translation) of the shared container. */
fun setTopPosition(top: Float) {
_topPosition.value = top
}
+ /** An internal modification was made to notifications */
+ fun notificationStackChanged() {
+ _notificationStackChanged.tryEmit(Unit)
+ }
+
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt
new file mode 100644
index 000000000000..98c173402109
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/DisplaySwitchNotificationsHiderFlag.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.stack.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the DisplaySwitchNotificationsHider flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object DisplaySwitchNotificationsHiderFlag {
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HIDE_ON_DISPLAY_SWITCH
+
+ /** Is the hiding enabled? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationsHideOnDisplaySwitch()
+
+ /**
+ * 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/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
new file mode 100644
index 000000000000..5a71bd6fa116
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.stack.shared
+
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+
+private const val FLEXI_NOTIFS = false
+
+/**
+ * Returns whether flexiglass is displaying notifications, which is currently an optional piece of
+ * flexiglass
+ */
+fun SceneContainerFlags.flexiNotifsEnabled() = FLEXI_NOTIFS && isEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
new file mode 100644
index 000000000000..274bf94566cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.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.statusbar.notification.stack.ui.viewbinder
+
+import androidx.core.view.doOnDetach
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
+import kotlinx.coroutines.launch
+
+/**
+ * Binds a [NotificationStackScrollLayoutController] to its [view model][NotificationListViewModel].
+ */
+object HideNotificationsBinder {
+ fun bindHideList(
+ viewController: NotificationStackScrollLayoutController,
+ viewModel: NotificationListViewModel
+ ) {
+ viewController.view.repeatWhenAttached {
+ lifecycleScope.launch {
+ viewModel.hideListViewModel.shouldHideListForPerformance.collect { shouldHide ->
+ viewController.bindHideState(shouldHide)
+ }
+ }
+ }
+
+ viewController.view.doOnDetach { viewController.bindHideState(shouldHide = false) }
+ }
+
+ private fun NotificationStackScrollLayoutController.bindHideState(shouldHide: Boolean) {
+ if (shouldHide) {
+ updateNotificationsContainerVisibility(/* visible= */ false, /* animate=*/ false)
+ setSuppressChildrenMeasureAndLayout(true)
+ } else {
+ setSuppressChildrenMeasureAndLayout(false)
+
+ // Show notifications back only after layout has finished because we need
+ // to wait until they have resized to the new display size
+ addOneShotPreDrawListener {
+ updateNotificationsContainerVisibility(/* visible= */ true, /* animate=*/ true)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index d55c0ded502d..4554085c35c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -17,9 +17,13 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.view.LayoutInflater
+import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.traceSection
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.nano.MetricsProto
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.reinflateAndBindLatest
+import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
@@ -27,28 +31,48 @@ import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
class NotificationListViewBinder
@Inject
constructor(
private val viewModel: NotificationListViewModel,
+ private val metricsLogger: MetricsLogger,
private val configuration: ConfigurationState,
private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
+ private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val shelfIconViewStore: ShelfNotificationIconViewStore,
) {
- fun bind(view: NotificationStackScrollLayout) {
+ fun bind(
+ view: NotificationStackScrollLayout,
+ viewController: NotificationStackScrollLayoutController
+ ) {
bindShelf(view)
bindFooter(view)
+ bindEmptyShade(view)
+ bindHideList(viewController, viewModel)
+
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
+ view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+ }
+ }
+ }
}
private fun bindShelf(parentView: NotificationStackScrollLayout) {
@@ -62,6 +86,7 @@ constructor(
configuration,
configurationController,
falsingManager,
+ iconViewBindingFailureTracker,
iconAreaController,
shelfIconViewStore,
)
@@ -78,7 +103,17 @@ constructor(
attachToRoot = false,
) { footerView: FooterView ->
traceSection("bind FooterView") {
- val disposableHandle = FooterViewBinder.bind(footerView, footerViewModel)
+ val disposableHandle =
+ FooterViewBinder.bind(
+ footerView,
+ footerViewModel,
+ clearAllNotifications = {
+ metricsLogger.action(
+ MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
+ )
+ parentView.clearAllNotifications()
+ },
+ )
parentView.setFooterView(footerView)
return@reinflateAndBindLatest disposableHandle
}
@@ -86,4 +121,26 @@ constructor(
}
}
}
+
+ private fun bindEmptyShade(
+ parentView: NotificationStackScrollLayout,
+ ) {
+ parentView.repeatWhenAttached {
+ lifecycleScope.launch {
+ combine(
+ viewModel.shouldShowEmptyShadeView,
+ viewModel.areNotificationsHiddenInShade,
+ viewModel.hasFilteredOutSeenNotifications,
+ ::Triple
+ )
+ .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
+ parentView.updateEmptyShadeView(
+ shouldShow,
+ areNotifsHidden,
+ hasFilteredNotifs,
+ )
+ }
+ }
+ }
+ }
}
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
new file mode 100644
index 000000000000..4d6a6ee98b7d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.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.statusbar.notification.stack.ui.viewbinder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+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 kotlinx.coroutines.launch
+
+/** Binds the shared notification container to its view-model. */
+object NotificationStackAppearanceViewBinder {
+
+ @JvmStatic
+ fun bind(
+ view: SharedNotificationContainer,
+ viewModel: NotificationStackAppearanceViewModel,
+ sceneContainerFlags: SceneContainerFlags,
+ ambientState: AmbientState,
+ controller: NotificationStackScrollLayoutController,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.stackPosition.collect {
+ controller.updateTopPadding(
+ it.top,
+ controller.isAddOrRemoveAnimationPending
+ )
+ }
+ }
+ launch {
+ viewModel.expandFraction.collect {
+ ambientState.expansionFraction = it
+ controller.expandedHeight = it * controller.view.height
+ }
+ }
+ }
+ }
+ }
+}
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 a1a0ccac3500..44006fc3fd4e 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
@@ -19,7 +19,10 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
+import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import kotlinx.coroutines.DisposableHandle
@@ -32,7 +35,9 @@ object SharedNotificationContainerBinder {
fun bind(
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
+ sceneContainerFlags: SceneContainerFlags,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -54,15 +59,24 @@ object SharedNotificationContainerBinder {
}
launch {
- viewModel.maxNotifications.collect {
- controller.setMaxDisplayedNotifications(it)
- }
+ viewModel
+ .getMaxNotifications { space ->
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.getView(),
+ space,
+ 0f, // Vertical space for shelf is already accounted for
+ controller.getShelfHeight().toFloat(),
+ )
+ }
+ .collect { controller.setMaxDisplayedNotifications(it) }
}
- launch {
- viewModel.position.collect {
- val animate = it.animate || controller.isAddOrRemoveAnimationPending()
- controller.updateTopPadding(it.top, animate)
+ if (!sceneContainerFlags.flexiNotifsEnabled()) {
+ launch {
+ viewModel.position.collect {
+ val animate = it.animate || controller.isAddOrRemoveAnimationPending
+ controller.updateTopPadding(it.top, animate)
+ }
}
}
@@ -70,9 +84,12 @@ object SharedNotificationContainerBinder {
}
}
+ controller.setOnHeightChangedRunnable(Runnable { viewModel.notificationStackChanged() })
+
return object : DisposableHandle {
override fun dispose() {
disposableHandle.dispose()
+ controller.setOnHeightChangedRunnable(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/HideListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/HideListViewModel.kt
new file mode 100644
index 000000000000..e1d14d1f5e0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/HideListViewModel.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.domain.interactor.HideNotificationsInteractor
+import com.android.systemui.statusbar.notification.stack.shared.DisplaySwitchNotificationsHiderFlag
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+@SysUISingleton
+class HideListViewModel
+@Inject
+constructor(
+ private val hideNotificationsInteractor: Provider<HideNotificationsInteractor>,
+) {
+ /**
+ * Emits `true` whenever we want to hide the notifications list for performance reasons, then it
+ * emits 'false' to show notifications back. This is used on foldable devices and emits
+ * *nothing* on other devices.
+ */
+ val shouldHideListForPerformance: Flow<Boolean>
+ get() =
+ if (DisplaySwitchNotificationsHiderFlag.isEnabled) {
+ hideNotificationsInteractor.get().shouldHideNotifications
+ } else emptyFlow()
+}
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 f01245f264e7..569ae248ad23 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
@@ -16,15 +16,101 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import java.util.Optional
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.onStart
/** ViewModel for the list of notifications. */
class NotificationListViewModel
@Inject
constructor(
val shelf: NotificationShelfViewModel,
+ val hideListViewModel: HideListViewModel,
val footer: Optional<FooterViewModel>,
-)
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ seenNotificationsInteractor: SeenNotificationsInteractor,
+ shadeInteractor: ShadeInteractor,
+ zenModeInteractor: ZenModeInteractor,
+) {
+ /**
+ * We want the NSSL to be unimportant for accessibility when there are no notifications in it
+ * while the device is on lock screen, to avoid an unlabelled NSSL view in TalkBack. Otherwise,
+ * we want it to be important for accessibility to enable accessibility auto-scrolling in NSSL.
+ * See b/242235264 for more details.
+ */
+ val isImportantForAccessibility: Flow<Boolean> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(true)
+ } else {
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ keyguardTransitionInteractor.isFinishedInStateWhere {
+ KeyguardState.lockscreenVisibleInState(it)
+ }
+ ) { hasNotifications, isOnKeyguard ->
+ hasNotifications || !isOnKeyguard
+ }
+ .distinctUntilChanged()
+ }
+ }
+
+ val shouldShowEmptyShadeView: Flow<Boolean> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(false)
+ } else {
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ shadeInteractor.isQsFullscreen,
+ keyguardTransitionInteractor.isInTransitionToState(KeyguardState.AOD).onStart {
+ emit(false)
+ },
+ keyguardTransitionInteractor
+ .isFinishedInState(KeyguardState.PRIMARY_BOUNCER)
+ .onStart { emit(false) }
+ ) { hasNotifications, isQsFullScreen, transitioningToAOD, isBouncerShowing ->
+ !hasNotifications &&
+ !isQsFullScreen &&
+ // Hide empty shade view when in transition to AOD.
+ // That avoids "No Notifications" blinking when transitioning to AOD.
+ // For more details, see b/228790482.
+ !transitioningToAOD &&
+ // Don't show any notification content if the bouncer is showing. See
+ // b/267060171.
+ !isBouncerShowing
+ }
+ .distinctUntilChanged()
+ }
+ }
+
+ // TODO(b/308591475): This should be tracked separately by the empty shade.
+ val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(false)
+ } else {
+ zenModeInteractor.areNotificationsHiddenInShade
+ }
+ }
+
+ // TODO(b/308591475): This should be tracked separately by the empty shade.
+ val hasFilteredOutSeenNotifications: Flow<Boolean> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(false)
+ } else {
+ seenNotificationsInteractor.hasFilteredOutSeenNotifications
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
new file mode 100644
index 000000000000..b86993486097
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
+@SysUISingleton
+class NotificationStackAppearanceViewModel
+@Inject
+constructor(
+ stackAppearanceInteractor: NotificationStackAppearanceInteractor,
+ shadeInteractor: ShadeInteractor,
+) {
+ /** The expansion fraction from the top of the notification shade */
+ val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
+
+ /** The position of the notification stack in the current scene */
+ val stackPosition: Flow<SharedNotificationContainerPosition> =
+ stackAppearanceInteractor.stackPosition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
new file mode 100644
index 000000000000..7def6feedb41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import javax.inject.Inject
+
+/**
+ * ViewModel used by the Notification placeholders inside the scene container to update the
+ * [NotificationStackAppearanceInteractor], and by extension control the NSSL.
+ */
+@SysUISingleton
+class NotificationsPlaceholderViewModel
+@Inject
+constructor(
+ private val interactor: NotificationStackAppearanceInteractor,
+ flags: SceneContainerFlags,
+ featureFlags: FeatureFlagsClassic,
+) {
+ /** DEBUG: whether the placeholder "Notifications" text should be shown. */
+ val isPlaceholderTextVisible: Boolean = !flags.flexiNotifsEnabled()
+
+ /** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
+ val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
+
+ /** DEBUG: whether the debug logging should be output. */
+ val isDebugLoggingEnabled: Boolean = flags.flexiNotifsEnabled()
+
+ /** Sets the position of the notification stack in the current scene */
+ fun setPlaceholderPositionInWindow(top: Float, bottom: Float) {
+ interactor.setStackPosition(SharedNotificationContainerPosition(top, bottom))
+ }
+}
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 b86b5dcc7939..09b4dfa31788 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
@@ -18,32 +18,36 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+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.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@Inject
constructor(
- interactor: SharedNotificationContainerInteractor,
+ private val interactor: SharedNotificationContainerInteractor,
+ @Application applicationScope: CoroutineScope,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- notificationStackSizeCalculator: NotificationStackSizeCalculator,
- controller: NotificationStackScrollLayoutController,
- shadeInteractor: ShadeInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
private val statesForConstrainedNotifications =
setOf(
@@ -105,32 +109,38 @@ constructor(
*
* When the shade is expanding, the position is controlled by... the shade.
*/
- val position: Flow<SharedNotificationContainerPosition> =
- isOnLockscreenWithoutShade.flatMapLatest { onLockscreen ->
- if (onLockscreen) {
- combine(
- keyguardInteractor.sharedNotificationContainerPosition,
- configurationBasedDimensions
- ) { position, config ->
- if (config.useSplitShade) {
- position.copy(top = 0f)
- } else {
- position
+ val position: StateFlow<SharedNotificationContainerPosition> =
+ isOnLockscreenWithoutShade
+ .flatMapLatest { onLockscreen ->
+ if (onLockscreen) {
+ combine(
+ keyguardInteractor.sharedNotificationContainerPosition,
+ configurationBasedDimensions
+ ) { position, config ->
+ if (config.useSplitShade) {
+ position.copy(top = 0f)
+ } else {
+ position
+ }
+ }
+ } else {
+ interactor.topPosition.sample(shadeInteractor.qsExpansion, ::Pair).map {
+ (top, qsExpansion) ->
+ // When QS expansion > 0, it should directly set the top padding so do not
+ // animate it
+ val animate = qsExpansion == 0f
+ keyguardInteractor.sharedNotificationContainerPosition.value.copy(
+ top = top,
+ animate = animate
+ )
}
- }
- } else {
- interactor.topPosition.sample(shadeInteractor.qsExpansion, ::Pair).map {
- (top, qsExpansion) ->
- // When QS expansion > 0, it should directly set the top padding so do not
- // animate it
- val animate = qsExpansion == 0f
- keyguardInteractor.sharedNotificationContainerPosition.value.copy(
- top = top,
- animate = animate
- )
}
}
- }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = SharedNotificationContainerPosition(0f, 0f),
+ )
/**
* Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
@@ -151,24 +161,45 @@ constructor(
* 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.
*
- * TODO: b/296606746 - Need to rerun logic when notifs change
+ * When expanding or when the user is interacting with the shade, keep the count stable; do not
+ * emit a value.
*/
- val maxNotifications: Flow<Int> =
- combine(isOnLockscreen, shadeInteractor.shadeExpansion, position) {
- onLockscreen,
- shadeExpansion,
- positionInfo ->
- if (onLockscreen && shadeExpansion < 1f) {
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- controller.getView(),
- positionInfo.bottom - positionInfo.top,
- 0f, // Vertical space for shelf is already accounted for
- controller.getShelfHeight().toFloat(),
- )
- } else {
- -1 // No limit
+ fun getMaxNotifications(calculateSpace: (Float) -> Int): Flow<Int> {
+ // When to limit notifications: on lockscreen with an unexpanded shade. Also, recalculate
+ // when the notification stack has changed internally
+ val limitedNotifications =
+ combine(
+ position,
+ interactor.notificationStackChanged.onStart { emit(Unit) },
+ ) { position, _ ->
+ calculateSpace(position.bottom - position.top)
}
- }
+
+ // When to show unlimited notifications: When the shade is fully expanded and the user is
+ // not actively dragging the shade
+ val unlimitedNotifications =
+ combineTransform(
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.isUserInteracting,
+ ) { shadeExpansion, isUserInteracting ->
+ if (shadeExpansion == 1f && !isUserInteracting) {
+ emit(-1)
+ }
+ }
+ return isOnLockscreenWithoutShade
+ .flatMapLatest { isOnLockscreenWithoutShade ->
+ if (isOnLockscreenWithoutShade) {
+ limitedNotifications
+ } else {
+ unlimitedNotifications
+ }
+ }
+ .distinctUntilChanged()
+ }
+
+ fun notificationStackChanged() {
+ interactor.notificationStackChanged()
+ }
data class ConfigurationBasedDimensions(
val marginStart: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt
new file mode 100644
index 000000000000..bcf732214d57
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.ui.viewbinder
+
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import dagger.Module
+
+@Module(includes = [StatusBarIconViewBindingFailureTracker.Module::class])
+object StatusBarNotificationViewBinderModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 3877bb6660ba..201133288de5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -36,6 +36,7 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -142,6 +143,7 @@ public class AutoTileManager implements UserAwareController {
* Init method must be called after construction to start listening
*/
public void init() {
+ QSPipelineFlagsRepository.Utils.assertInLegacyMode();
if (mInitialized) {
Log.w(TAG, "Trying to re-initialize");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index cbe9d4b93ead..4e77801af515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -49,7 +49,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -186,8 +185,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
- private final FeatureFlags mFeatureFlags;
-
private static final class PendingAuthenticated {
public final int userId;
public final BiometricSourceType biometricSourceType;
@@ -291,7 +288,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
ScreenOffAnimationController screenOffAnimationController,
VibratorHelper vibrator,
SystemClock systemClock,
- FeatureFlags featureFlags,
DeviceEntryHapticsInteractor hapticsInteractor,
Lazy<SelectedUserInteractor> selectedUserInteractor,
BiometricUnlockInteractor biometricUnlockInteractor
@@ -322,7 +318,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mVibratorHelper = vibrator;
mLogger = biometricUnlockLogger;
mSystemClock = systemClock;
- mFeatureFlags = featureFlags;
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
mHapticsInteractor = hapticsInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 22b9298b629d..60a4606ef0d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -42,25 +41,23 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -97,7 +94,6 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final PowerManager mPowerManager;
- private final VibratorHelper mVibratorHelper;
private final Optional<Vibrator> mVibratorOptional;
private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
@@ -108,8 +104,6 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final QuickSettingsController mQsController;
private final QSHost mQSHost;
- private final FeatureFlags mFeatureFlags;
-
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -139,15 +133,13 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
PowerManager powerManager,
- VibratorHelper vibratorHelper,
Optional<Vibrator> vibratorOptional,
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
Lazy<CameraLauncher> cameraLauncherLazy,
UserTracker userTracker,
QSHost qsHost,
- ActivityStarter activityStarter,
- FeatureFlags featureFlags) {
+ ActivityStarter activityStarter) {
mCentralSurfaces = centralSurfaces;
mQsController = quickSettingsController;
mContext = context;
@@ -168,14 +160,12 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mPowerManager = powerManager;
- mVibratorHelper = vibratorHelper;
mVibratorOptional = vibratorOptional;
mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mCameraLauncherLazy = cameraLauncherLazy;
mUserTracker = userTracker;
mQSHost = qsHost;
- mFeatureFlags = featureFlags;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -544,12 +534,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
@VisibleForTesting
void vibrateOnNavigationKeyDown() {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mShadeViewController.performHapticFeedback(
- HapticFeedbackConstants.GESTURE_START
- );
- } else {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
- }
+ mShadeViewController.performHapticFeedback(
+ HapticFeedbackConstants.GESTURE_START
+ );
}
}
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 2e5717de1a86..645769cdd586 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.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_
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.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -129,6 +130,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -139,6 +141,7 @@ 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;
@@ -159,6 +162,7 @@ import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -202,7 +206,7 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
@@ -236,6 +240,8 @@ import com.android.wm.shell.startingsurface.StartingSurface;
import dalvik.annotation.optimization.NeverCompile;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -247,8 +253,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
-import dagger.Lazy;
-
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -454,7 +458,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final NotificationGutsManager mGutsManager;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final KeyguardViewMediator mKeyguardViewMediator;
- protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
private final FragmentService mFragmentService;
@@ -583,6 +587,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final InteractionJankMonitor mJankMonitor;
+ private final SceneContainerFlags mSceneContainerFlags;
+
/**
* Public constructor for CentralSurfaces.
*
@@ -613,7 +619,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
NotificationGutsManager notificationGutsManager,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
+ VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ShadeExpansionStateManager shadeExpansionStateManager,
KeyguardViewMediator keyguardViewMediator,
DisplayMetrics displayMetrics,
@@ -696,7 +702,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
AlternateBouncerInteractor alternateBouncerInteractor,
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager,
- ActivityStarter activityStarter
+ ActivityStarter activityStarter,
+ SceneContainerFlags sceneContainerFlags
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -719,7 +726,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mGutsManager = notificationGutsManager;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+ mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider;
mShadeExpansionStateManager = shadeExpansionStateManager;
mKeyguardViewMediator = keyguardViewMediator;
mDisplayMetrics = displayMetrics;
@@ -793,6 +800,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mUserTracker = userTracker;
mFingerprintManager = fingerprintManager;
mActivityStarter = activityStarter;
+ mSceneContainerFlags = sceneContainerFlags;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -946,7 +954,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onKeyguardGoingAwayChanged() {
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
// This code path is not used if the KeyguardTransitionRepository is managing
// the lightreveal scrim.
return;
@@ -1215,7 +1223,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
});
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
LightRevealScrimViewBinder.bind(
mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
}
@@ -1247,7 +1255,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// Set up the quick settings tile panel
final View container = getNotificationShadeWindowView().findViewById(R.id.qs_frame);
- if (container != null) {
+ if (container != null && !mSceneContainerFlags.isEnabled()) {
FragmentHostManager fragmentHostManager =
mFragmentService.getFragmentHostManager(container);
ExtensionFragmentListener.attachExtensonToFragment(
@@ -1449,7 +1457,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mShadeController.onStatusBarTouch(event);
}
return getNotificationShadeWindowView().onTouchEvent(event);
@@ -2348,7 +2356,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
return;
}
@@ -2771,7 +2779,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled()) {
if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
&& (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f)) {
@@ -2994,7 +3002,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (!lightRevealMigration()) {
mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
}
}
@@ -3149,7 +3157,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
+ if (!lightRevealMigration()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index beeee1be401d..495b4e1e14cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -252,7 +252,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
}
if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
- newEntry == null ? null : newEntry.getKey());
+ newEntry == null ? null : newEntry.getRepresentativeEntry().getKey());
} else {
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
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 3b3d8b6b2109..a62a1ed9f0c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
+import static com.android.systemui.Flags.newAodTransition;
import android.content.Context;
import android.content.res.Resources;
@@ -40,7 +40,7 @@ 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.flags.Flags;
+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;
@@ -106,16 +106,12 @@ public class LegacyNotificationIconAreaControllerImpl implements
private NotificationIconContainer mAodIcons;
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
-
- private final boolean mNewAodTransition;
-
private int mAodIconAppearTranslation;
private boolean mAnimationsEnabled;
private int mAodIconTint;
private boolean mAodIconsVisible;
private boolean mShowLowPriority = true;
- private boolean mIsStatusViewMigrated = false;
@VisibleForTesting
final NotificationListener.NotificationSettingsListener mSettingsListener =
@@ -146,7 +142,6 @@ public class LegacyNotificationIconAreaControllerImpl implements
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mNewAodTransition = featureFlags.isEnabled(NEW_AOD_TRANSITION);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -159,7 +154,6 @@ public class LegacyNotificationIconAreaControllerImpl implements
mStatusBarWindowController = statusBarWindowController;
mScreenOffAnimationController = screenOffAnimationController;
notificationListener.addNotificationSettingsListener(mSettingsListener);
- mIsStatusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW);
initializeNotificationAreaViews(context);
reloadAodColor();
darkIconDispatcher.addDarkReceiver(this);
@@ -551,7 +545,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
return;
}
if (mScreenOffAnimationController.shouldAnimateAodIcons()) {
- if (!mIsStatusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
}
mAodIcons.setAlpha(0);
@@ -563,14 +557,14 @@ public class LegacyNotificationIconAreaControllerImpl implements
.start();
} else {
mAodIcons.setAlpha(1.0f);
- if (!mIsStatusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mAodIcons.setTranslationY(0);
}
}
}
private void animateInAodIconTranslation() {
- if (!mIsStatusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mAodIcons.animate()
.setInterpolator(Interpolators.DECELERATE_QUINT)
.translationY(0)
@@ -602,7 +596,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
boolean animate = true;
if (!mBypassController.getBypassEnabled()) {
animate = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking();
- if (!mNewAodTransition) {
+ if (!newAodTransition()) {
// We only want the appear animations to happen when the notifications get fully
// hidden, since otherwise the unhide animation overlaps
animate &= fullyHidden;
@@ -642,7 +636,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
mAodIconsVisible = visible;
mAodIcons.animate().cancel();
if (animate) {
- if (mNewAodTransition) {
+ if (newAodTransition()) {
// Let's make sure the icon are translated to 0, since we cancelled it above
animateInAodIconTranslation();
if (mAodIconsVisible) {
@@ -673,7 +667,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
}
} else {
mAodIcons.setAlpha(1.0f);
- if (!mIsStatusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
mAodIcons.setTranslationY(0);
}
mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index c20732471cf4..2235035fe5aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -24,6 +24,8 @@ import android.os.SystemClock;
import android.util.MathUtils;
import android.util.TimeUtils;
+import androidx.annotation.VisibleForTesting;
+
import com.android.app.animation.Interpolators;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dumpable;
@@ -96,13 +98,14 @@ public class LightBarTransitionsController implements Dumpable {
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private final CommandQueue mCommandQueue;
- private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+ private GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private boolean mTransitionDeferring;
private long mTransitionDeferringStartTime;
private long mTransitionDeferringDuration;
private boolean mTransitionPending;
private boolean mTintChangePending;
+ private boolean mNavigationButtonsForcedVisible;
private float mPendingDarkIntensity;
private ValueAnimator mTintAnimator;
private float mDarkIntensity;
@@ -137,13 +140,16 @@ public class LightBarTransitionsController implements Dumpable {
mContext = context;
mDisplayId = mContext.getDisplayId();
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
- mHandler, mContext, null);
+ mHandler, mContext, this::onNavigationSettingsChanged);
+ mGestureNavigationSettingsObserver.register();
+ onNavigationSettingsChanged();
}
/** Call to cleanup the LightBarTransitionsController when done with it. */
public void destroy() {
mCommandQueue.removeCallback(mCallback);
mStatusBarStateController.removeCallback(mCallback);
+ mGestureNavigationSettingsObserver.unregister();
}
public void saveState(Bundle outState) {
@@ -199,6 +205,12 @@ public class LightBarTransitionsController implements Dumpable {
mTransitionPending = false;
}
+ @VisibleForTesting
+ void setNavigationSettingsObserver(GestureNavigationSettingsObserver observer) {
+ mGestureNavigationSettingsObserver = observer;
+ onNavigationSettingsChanged();
+ }
+
public void setIconsDark(boolean dark, boolean animate) {
if (!animate) {
setIconTintInternal(dark ? 1.0f : 0.0f);
@@ -253,6 +265,28 @@ public class LightBarTransitionsController implements Dumpable {
mApplier.applyDarkIntensity(MathUtils.lerp(mDarkIntensity, 0f, mDozeAmount));
}
+ public void onDozeAmountChanged(float linear, float eased) {
+ mDozeAmount = eased;
+ dispatchDark();
+ }
+
+ /**
+ * Called when the navigation settings change.
+ */
+ private void onNavigationSettingsChanged() {
+ mNavigationButtonsForcedVisible =
+ mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+ }
+
+ /**
+ * Return whether to use the tint calculated in this class for nav icons.
+ */
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // In gesture mode, we already do region sampling to update tint based on content beneath.
+ return !QuickStepContract.isGesturalMode(navigationMode)
+ || mNavigationButtonsForcedVisible;
+ }
+
@Override
public void dump(PrintWriter pw, String[] args) {
pw.print(" mTransitionDeferring="); pw.print(mTransitionDeferring);
@@ -271,20 +305,7 @@ public class LightBarTransitionsController implements Dumpable {
pw.print(" mPendingDarkIntensity="); pw.print(mPendingDarkIntensity);
pw.print(" mDarkIntensity="); pw.print(mDarkIntensity);
pw.print(" mNextDarkIntensity="); pw.println(mNextDarkIntensity);
- }
-
- public void onDozeAmountChanged(float linear, float eased) {
- mDozeAmount = eased;
- dispatchDark();
- }
-
- /**
- * Return whether to use the tint calculated in this class for nav icons.
- */
- public boolean supportsIconTintForNavMode(int navigationMode) {
- // In gesture mode, we already do region sampling to update tint based on content beneath.
- return !QuickStepContract.isGesturalMode(navigationMode)
- || mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+ pw.print(" mAreNavigationButtonForcedVisible="); pw.println(mNavigationButtonsForcedVisible);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9e5fd959f2d0..00e78a49ba19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -141,8 +141,10 @@ public class NotificationIconContainer extends ViewGroup {
private int mMaxStaticIcons;
private boolean mDozing;
private boolean mOnLockScreen;
- private boolean mOverrideIconColor;
+ private int mSpeedBumpIndex = -1;
+ private int mMaxIcons = Integer.MAX_VALUE;
+ private boolean mOverrideIconColor;
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
private int mDotPadding;
@@ -153,7 +155,6 @@ public class NotificationIconContainer extends ViewGroup {
private boolean mChangingViewPositions;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
- private int mSpeedBumpIndex = -1;
private int mIconSize;
private boolean mDisallowNextAnimation;
private boolean mAnimationsEnabled = true;
@@ -170,6 +171,7 @@ public class NotificationIconContainer extends ViewGroup {
private View mIsolatedIconForAnimation;
private int mThemedTextColorPrimary;
private Runnable mIsolatedIconAnimationEndRunnable;
+ private boolean mUseIncreasedIconScale;
public NotificationIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -436,18 +438,24 @@ public class NotificationIconContainer extends ViewGroup {
if (numIcons == 0) {
return 0f;
}
- final float contentWidth =
- mIconSize * MathUtils.min(numIcons, mMaxIconsOnLockscreen + 1);
- return getActualPaddingStart()
- + contentWidth
- + getActualPaddingEnd();
+ final float contentWidth;
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ contentWidth = mIconSize * numIcons;
+ } else {
+ contentWidth = mIconSize * MathUtils.min(numIcons, mMaxIconsOnLockscreen + 1);
+ }
+ return getActualPaddingStart() + contentWidth + getActualPaddingEnd();
}
@VisibleForTesting
boolean shouldForceOverflow(int i, int speedBumpIndex, float iconAppearAmount,
int maxVisibleIcons) {
- return speedBumpIndex != -1 && i >= speedBumpIndex
- && iconAppearAmount > 0.0f || i >= maxVisibleIcons;
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ return i >= maxVisibleIcons && iconAppearAmount > 0.0f;
+ } else {
+ return speedBumpIndex != -1 && i >= speedBumpIndex
+ && iconAppearAmount > 0.0f || i >= maxVisibleIcons;
+ }
}
@VisibleForTesting
@@ -502,9 +510,8 @@ public class NotificationIconContainer extends ViewGroup {
firstOverflowIndex = i;
mVisualOverflowStart = translationX;
}
- final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
- ? ((StatusBarIconView) view).getIconScaleIncreased()
- : 1f;
+
+ final float drawingScale = getDrawingScale(view);
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
mIsShowingOverflowDot = false;
@@ -554,9 +561,26 @@ public class NotificationIconContainer extends ViewGroup {
}
}
+ private float getDrawingScale(View view) {
+ final boolean useIncreasedScale = NotificationIconContainerRefactor.isEnabled()
+ ? mUseIncreasedIconScale
+ : mOnLockScreen;
+ return useIncreasedScale && view instanceof StatusBarIconView
+ ? ((StatusBarIconView) view).getIconScaleIncreased()
+ : 1f;
+ }
+
+ public void setUseIncreasedIconScale(boolean useIncreasedIconScale) {
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
+ mUseIncreasedIconScale = useIncreasedIconScale;
+ }
+
private int getMaxVisibleIcons(int childCount) {
- return mOnLockScreen ? mMaxIconsOnAod :
- mIsStaticLayout ? mMaxStaticIcons : childCount;
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ return mMaxIcons;
+ } else {
+ return mOnLockScreen ? mMaxIconsOnAod : mIsStaticLayout ? mMaxStaticIcons : childCount;
+ }
}
private float getLayoutEnd() {
@@ -673,9 +697,15 @@ public class NotificationIconContainer extends ViewGroup {
}
public void setSpeedBumpIndex(int speedBumpIndex) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
mSpeedBumpIndex = speedBumpIndex;
}
+ public void setMaxIconsAmount(int maxIcons) {
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
+ mMaxIcons = maxIcons;
+ }
+
public int getIconSize() {
return mIconSize;
}
@@ -740,6 +770,7 @@ public class NotificationIconContainer extends ViewGroup {
* configured to. Depending on these values, the layout of the AOD icons change.
*/
public void setOnLockScreen(boolean onLockScreen) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
mOnLockScreen = onLockScreen;
}
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 3e753a589a84..ae04eaf49b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -396,9 +396,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
}
- mScrimBehind.setDefaultFocusHighlightEnabled(false);
- mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
- mScrimInFront.setDefaultFocusHighlightEnabled(false);
mTransparentScrimBackground = notificationsScrim.getResources()
.getBoolean(R.bool.notification_scrim_transparent);
updateScrims();
@@ -509,12 +506,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
applyState();
- // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
- // We need to disable focus otherwise AOD would end up with a gray overlay.
- mScrimInFront.setFocusable(!state.isLowPowerState());
- mScrimBehind.setFocusable(!state.isLowPowerState());
- mNotificationsScrim.setFocusable(!state.isLowPowerState());
-
mScrimInFront.setBlendWithMainColor(state.shouldBlendWithMainColor());
// Cancel blanking transitions that were pending before we requested a new state
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 267b56378d82..274b50fd79fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -62,6 +62,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.bouncer.ui.BouncerView;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
@@ -1573,7 +1574,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* notification shade's child views.
*/
public boolean shouldInterceptTouchEvent(MotionEvent event) {
- if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
return false;
}
return mAlternateBouncerInteractor.isVisibleState();
@@ -1584,7 +1585,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* showing.
*/
public boolean onTouch(MotionEvent event) {
- if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
return false;
}
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 dbee080f238d..2e1a0770757b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -72,7 +72,6 @@ import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorCon
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
@@ -115,7 +114,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final com.android.systemui.shade.ShadeController mShadeController;
private final KeyguardStateController mKeyguardStateController;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final LockPatternUtils mLockPatternUtils;
private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
private final ActivityIntentHelper mActivityIntentHelper;
@@ -154,7 +152,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
NotificationLockscreenUserManager lockscreenUserManager,
ShadeController shadeController,
KeyguardStateController keyguardStateController,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
LockPatternUtils lockPatternUtils,
StatusBarRemoteInputCallback remoteInputCallback,
ActivityIntentHelper activityIntentHelper,
@@ -187,7 +184,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mLockscreenUserManager = lockscreenUserManager;
mShadeController = shadeController;
mKeyguardStateController = keyguardStateController;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mLockPatternUtils = lockPatternUtils;
mStatusBarRemoteInputCallback = remoteInputCallback;
mActivityIntentHelper = activityIntentHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 07e2571bcb38..8e9c0384987d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,6 +14,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -29,6 +32,8 @@ import android.util.Log;
import android.util.Slog;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
import com.android.systemui.dagger.SysUISingleton;
@@ -54,7 +59,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
@@ -63,6 +71,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.Set;
+
import javax.inject.Inject;
@SysUISingleton
@@ -163,7 +173,14 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
initController.addPostInitTask(() -> {
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
- visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor);
+ if (VisualInterruptionRefactor.isEnabled()) {
+ visualInterruptionDecisionProvider.addCondition(mAlertsDisabledCondition);
+ visualInterruptionDecisionProvider.addCondition(mVrModeCondition);
+ visualInterruptionDecisionProvider.addFilter(mNeedsRedactionFilter);
+ visualInterruptionDecisionProvider.addCondition(mPanelsDisabledCondition);
+ } else {
+ visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor);
+ }
mLockscreenUserManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(
this, mNotifListContainer, mOnSettingsClickListener);
@@ -306,4 +323,54 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
return !mNotificationAlertsInteractor.areNotificationAlertsEnabled();
}
};
+
+ private final VisualInterruptionCondition mAlertsDisabledCondition =
+ new VisualInterruptionCondition(Set.of(PEEK, PULSE, BUBBLE),
+ "notification alerts disabled") {
+ @Override
+ public boolean shouldSuppress() {
+ return !mNotificationAlertsInteractor.areNotificationAlertsEnabled();
+ }
+ };
+
+ private final VisualInterruptionCondition mVrModeCondition =
+ new VisualInterruptionCondition(Set.of(PEEK, BUBBLE), "device is in VR mode") {
+ @Override
+ public boolean shouldSuppress() {
+ return isDeviceInVrMode();
+ }
+ };
+
+ private final VisualInterruptionFilter mNeedsRedactionFilter =
+ new VisualInterruptionFilter(Set.of(PEEK), "needs redaction on public lockscreen") {
+ @Override
+ public boolean shouldSuppress(@NonNull NotificationEntry entry) {
+ if (!mKeyguardStateController.isOccluded()) {
+ return false;
+ }
+
+ if (!mLockscreenUserManager.needsRedaction(entry)) {
+ return false;
+ }
+
+ final int currentUserId = mLockscreenUserManager.getCurrentUserId();
+ final boolean currentUserPublic = mLockscreenUserManager.isLockscreenPublicMode(
+ currentUserId);
+
+ final int notificationUserId = entry.getSbn().getUserId();
+ final boolean notificationUserPublic =
+ mLockscreenUserManager.isLockscreenPublicMode(notificationUserId);
+
+ // TODO(b/135046837): we can probably relax this with dynamic privacy
+ return currentUserPublic || notificationUserPublic;
+ }
+ };
+
+ private final VisualInterruptionCondition mPanelsDisabledCondition =
+ new VisualInterruptionCondition(Set.of(PEEK), "disabled panel") {
+ @Override
+ public boolean shouldSuppress() {
+ return !mCommandQueue.panelsEnabled();
+ }
+ };
}
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 fb586eac5ab7..1a17e7c28410 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -20,6 +20,7 @@ import com.android.app.animation.Interpolators
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
@@ -34,8 +35,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.app.tracing.TraceUtils
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
/**
* When to show the keyguard (AOD) view. This should be once the light reveal scrim is barely
@@ -68,7 +67,6 @@ class UnlockedScreenOffAnimationController @Inject constructor(
private val interactionJankMonitor: InteractionJankMonitor,
private val powerManager: PowerManager,
private val handler: Handler = Handler(),
- private val featureFlags: FeatureFlags,
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
private lateinit var centralSurfaces: CentralSurfaces
private lateinit var shadeViewController: ShadeViewController
@@ -288,7 +286,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// up, with unpredictable consequences.
if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY) &&
shouldAnimateInKeyguard) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
// 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/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 3921e69501d2..7adc08ca00c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableSta
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
@@ -217,6 +218,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mWaitingForWindowStateChangeAfterCameraLaunch = false;
mTransitionFromLockscreenToDreamStarted = false;
};
+ private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
@Inject
public CollapsedStatusBarFragment(
@@ -235,6 +237,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController,
StatusBarStateController statusBarStateController,
+ StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
CommandQueue commandQueue,
CarrierConfigTracker carrierConfigTracker,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
@@ -264,6 +267,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mKeyguardStateController = keyguardStateController;
mShadeViewController = shadeViewController;
mStatusBarStateController = statusBarStateController;
+ mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mCommandQueue = commandQueue;
mCarrierConfigTracker = carrierConfigTracker;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
@@ -471,6 +475,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mStatusBarIconsViewModel,
mConfigurationState,
mConfigurationController,
+ mIconViewBindingFailureTracker,
mStatusBarIconViewStore);
} else {
mNotificationIconAreaInner =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 3522b9a13989..4f702d7534cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -109,8 +109,9 @@ constructor(
{
int1 = subId
str1 = displayInfo.toString()
+ bool1 = displayInfo.isRoaming
},
- { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" },
+ { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1 isRoaming=$bool1" },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 125fd9be15c7..4fb99c24c8ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -46,6 +46,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
@@ -105,6 +107,7 @@ class MobileConnectionRepositoryImpl(
private val bgDispatcher: CoroutineDispatcher,
logger: MobileInputLogger,
override val tableLogBuffer: TableLogBuffer,
+ flags: FeatureFlagsClassic,
scope: CoroutineScope,
) : MobileConnectionRepository {
init {
@@ -201,9 +204,15 @@ class MobileConnectionRepositoryImpl(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isRoaming =
- callbackEvents
- .mapNotNull { it.onServiceStateChanged }
- .map { it.serviceState.roaming }
+ if (flags.isEnabled(ROAMING_INDICATOR_VIA_DISPLAY_INFO)) {
+ callbackEvents
+ .mapNotNull { it.onDisplayInfoChanged }
+ .map { it.telephonyDisplayInfo.isRoaming }
+ } else {
+ callbackEvents
+ .mapNotNull { it.onServiceStateChanged }
+ .map { it.serviceState.roaming }
+ }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val operatorAlphaShort =
@@ -432,6 +441,7 @@ class MobileConnectionRepositoryImpl(
private val logger: MobileInputLogger,
private val carrierConfigRepository: CarrierConfigRepository,
private val mobileMappingsProxy: MobileMappingsProxy,
+ private val flags: FeatureFlagsClassic,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
) {
@@ -456,6 +466,7 @@ class MobileConnectionRepositoryImpl(
bgDispatcher,
logger,
mobileLogger,
+ flags,
scope,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
index a052008d4832..a052008d4832 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt
index 22d048343bc9..a2f5701d7eca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt
@@ -16,15 +16,41 @@
package com.android.systemui.statusbar.pipeline.mobile.util
+import android.annotation.SuppressLint
+import android.content.Context
+import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
interface SubscriptionManagerProxy {
fun getDefaultDataSubscriptionId(): Int
+ fun isValidSubscriptionId(subId: Int): Boolean
+ suspend fun getActiveSubscriptionInfo(subId: Int): SubscriptionInfo?
}
/** Injectable proxy class for [SubscriptionManager]'s static methods */
-class SubscriptionManagerProxyImpl @Inject constructor() : SubscriptionManagerProxy {
+class SubscriptionManagerProxyImpl
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val subscriptionManager: SubscriptionManager,
+) : SubscriptionManagerProxy {
/** The system default data subscription id, or INVALID_SUBSCRIPTION_ID on error */
override fun getDefaultDataSubscriptionId() = SubscriptionManager.getDefaultDataSubscriptionId()
+
+ override fun isValidSubscriptionId(subId: Int): Boolean {
+ return SubscriptionManager.isValidSubscriptionId(subId)
+ }
+
+ @SuppressLint("MissingPermission")
+ override suspend fun getActiveSubscriptionInfo(subId: Int): SubscriptionInfo? {
+ return withContext(backgroundDispatcher) {
+ subscriptionManager.getActiveSubscriptionInfo(subId)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index b614b6d0547d..78954dea27ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -35,13 +35,12 @@ import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.res.R;
import com.android.systemui.animation.Expandable;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.user.UserSwitchDialogController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -149,7 +148,6 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
UserSwitchDialogController userSwitchDialogController,
- FeatureFlags featureFlags,
UiEventLogger uiEventLogger) {
super(view);
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
@@ -163,7 +161,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
screenOffAnimationController, /* animateYPos= */ false,
- featureFlags, /* logBuffer= */ null);
+ /* logBuffer= */ null);
mUserSwitchDialogController = userSwitchDialogController;
mUiEventLogger = uiEventLogger;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index dfe26865f978..770f441799b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -39,7 +39,6 @@ import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -161,7 +160,6 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- FeatureFlags featureFlags,
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController) {
super(keyguardUserSwitcherView);
@@ -177,7 +175,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
screenOffAnimationController, /* animateYPos= */ false,
- featureFlags, /* logBuffer= */ null);
+ /* logBuffer= */ null);
mBackground = new KeyguardUserSwitcherScrim(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index d66ad5868e65..78f48bb511e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.policy
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AlarmTile
import com.android.systemui.qs.tiles.CameraToggleTile
@@ -23,8 +25,18 @@ import com.android.systemui.qs.tiles.LocationTile
import com.android.systemui.qs.tiles.MicrophoneToggleTile
import com.android.systemui.qs.tiles.UiModeNightTile
import com.android.systemui.qs.tiles.WorkModeTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.flashlight.domain.FlashlightMapper
+import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@@ -40,6 +52,42 @@ interface PolicyModule {
@StringKey(WorkModeTile.TILE_SPEC)
fun bindWorkModeTile(workModeTile: WorkModeTile): QSTileImpl<*>
+ companion object {
+ const val FLASHLIGHT_TILE_SPEC = "flashlight"
+
+ /** Inject config */
+ @Provides
+ @IntoMap
+ @StringKey(FLASHLIGHT_TILE_SPEC)
+ fun provideFlashlightTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(FLASHLIGHT_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_flashlight_icon_off,
+ labelRes = R.string.quick_settings_flashlight_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /** Inject FlashlightTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(FLASHLIGHT_TILE_SPEC)
+ fun provideFlashlightTileViewModel(
+ factory: QSTileViewModelFactory.Static<FlashlightTileModel>,
+ mapper: FlashlightMapper,
+ stateInteractor: FlashlightTileDataInteractor,
+ userActionInteractor: FlashlightTileUserActionInteractor
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(FLASHLIGHT_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ }
+
/** Inject FlashlightTile into tileMap in QSModule */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 818ba9512e15..3304b9827fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl;
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule;
+import com.android.systemui.statusbar.policy.data.repository.ZenModeRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -81,7 +82,7 @@ import java.util.concurrent.Executor;
import javax.inject.Named;
/** Dagger Module for code in the statusbar.policy package. */
-@Module(includes = { DeviceProvisioningRepositoryModule.class })
+@Module(includes = { DeviceProvisioningRepositoryModule.class, ZenModeRepositoryModule.class })
public interface StatusBarPolicyModule {
String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
new file mode 100644
index 000000000000..94ab58ae5a3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.policy.data.repository
+
+import android.app.NotificationManager
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.ZenModeController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A repository that holds information about the status and configuration of Zen Mode (or Do Not
+ * Disturb/DND Mode).
+ */
+interface ZenModeRepository {
+ val zenMode: Flow<Int>
+ val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?>
+}
+
+class ZenModeRepositoryImpl
+@Inject
+constructor(
+ private val zenModeController: ZenModeController,
+) : ZenModeRepository {
+ // TODO(b/308591859): ZenModeController should use flows instead of callbacks. The
+ // conflatedCallbackFlows here should be replaced eventually, see:
+ // https://docs.google.com/document/d/1gAiuYupwUAFdbxkDXa29A4aFNu7XoCd7sCIk31WTnHU/edit?resourcekey=0-J4ZBiUhLhhQnNobAcI2vIw
+
+ override val zenMode: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : ZenModeController.Callback {
+ override fun onZenChanged(zen: Int) {
+ trySend(zen)
+ }
+ }
+ zenModeController.addCallback(callback)
+ trySend(zenModeController.zen)
+
+ awaitClose { zenModeController.removeCallback(callback) }
+ }
+
+ override val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?> =
+ conflatedCallbackFlow {
+ val callback =
+ object : ZenModeController.Callback {
+ override fun onConsolidatedPolicyChanged(policy: NotificationManager.Policy?) {
+ trySend(policy)
+ }
+ }
+ zenModeController.addCallback(callback)
+ trySend(zenModeController.consolidatedPolicy)
+
+ awaitClose { zenModeController.removeCallback(callback) }
+ }
+}
+
+@Module
+interface ZenModeRepositoryModule {
+ @Binds fun bindImpl(impl: ZenModeRepositoryImpl): ZenModeRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
new file mode 100644
index 000000000000..ae31851cb8f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -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.systemui.statusbar.policy.domain.interactor
+
+import android.provider.Settings
+import com.android.systemui.statusbar.policy.data.repository.ZenModeRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/**
+ * An interactor that performs business logic related to the status and configuration of Zen Mode
+ * (or Do Not Disturb/DND Mode).
+ */
+class ZenModeInteractor @Inject constructor(repository: ZenModeRepository) {
+ val isZenModeEnabled: Flow<Boolean> =
+ repository.zenMode
+ .map {
+ when (it) {
+ Settings.Global.ZEN_MODE_ALARMS -> true
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS -> true
+ Settings.Global.ZEN_MODE_NO_INTERRUPTIONS -> true
+ Settings.Global.ZEN_MODE_OFF -> false
+ else -> false
+ }
+ }
+ .distinctUntilChanged()
+
+ val areNotificationsHiddenInShade: Flow<Boolean> =
+ combine(isZenModeEnabled, repository.consolidatedNotificationPolicy) { dndEnabled, policy ->
+ if (!dndEnabled) {
+ false
+ } else {
+ val showInNotificationList = policy?.showInNotificationList() ?: true
+ !showInNotificationList
+ }
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt
new file mode 100644
index 000000000000..4cbdd6fd6488
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.ui.binder
+
+import com.android.systemui.statusbar.notification.ui.viewbinder.StatusBarNotificationViewBinderModule
+import dagger.Module
+
+@Module(includes = [StatusBarNotificationViewBinderModule::class]) object StatusBarViewBinderModule
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index fc414b66b042..2f2c4b0530fb 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
-import com.android.systemui.res.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -48,9 +47,8 @@ import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -96,7 +94,6 @@ constructor(
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
tempViewUiEventLogger: TemporaryViewUiEventLogger,
- private val featureFlags: FeatureFlags,
) :
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
@@ -234,18 +231,14 @@ constructor(
maybeGetAccessibilityFocus(newInfo, currentView)
// ---- Haptics ----
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(parent, newInfo.vibrationConstant)
- } else {
- newInfo.vibrationEffect?.let {
- vibratorHelper.vibrate(
- Process.myUid(),
- context.getApplicationContext().getPackageName(),
- it,
- newInfo.windowTitle,
- VIBRATION_ATTRIBUTES,
- )
- }
+ newInfo.vibrationEffect?.let {
+ vibratorHelper.vibrate(
+ Process.myUid(),
+ context.getApplicationContext().getPackageName(),
+ it,
+ newInfo.windowTitle,
+ VIBRATION_ATTRIBUTES,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 4449f8d06f15..7475388e80c2 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -17,13 +17,12 @@
package com.android.systemui.temporarydisplay.chipbar
import android.os.VibrationEffect
-import android.view.HapticFeedbackConstants
import android.view.View
import androidx.annotation.AttrRes
import com.android.internal.logging.InstanceId
-import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.res.R
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.temporarydisplay.ViewPriority
@@ -43,7 +42,6 @@ data class ChipbarInfo(
val text: Text,
val endItem: ChipbarEndItem?,
val vibrationEffect: VibrationEffect? = null,
- val vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
val allowSwipeToDismiss: Boolean = false,
override val windowTitle: String,
override val wakeReason: String,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 2afb43515be7..36a1e8a072c9 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -49,6 +49,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.concurrency.ThreadFactory
import com.android.app.tracing.traceSection
+import com.android.keyguard.logging.ScrimLogger
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
import java.util.concurrent.Executor
@@ -69,7 +70,8 @@ constructor(
@Main private val executor: Executor,
private val threadFactory: ThreadFactory,
private val rotationChangeProvider: RotationChangeProvider,
- private val displayTracker: DisplayTracker
+ private val displayTracker: DisplayTracker,
+ private val scrimLogger: ScrimLogger,
) {
private val transitionListener = TransitionListener()
@@ -179,8 +181,8 @@ constructor(
)
.apply {
revealEffect = createLightRevealEffect()
- isScrimOpaqueChangedListener = Consumer {}
revealAmount = calculateRevealAmount()
+ scrimLogger = this@UnfoldLightRevealOverlayAnimation.scrimLogger
}
newRoot.setView(newView, params)
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 71314f1f1775..7b628f8d676f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -24,6 +24,10 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldStateProvider
@@ -149,8 +153,7 @@ class UnfoldTransitionModule {
return resultingProvider?.get()?.orElse(null)?.let { unfoldProgressProvider ->
UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
- }
- ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ } ?: ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
@@ -162,6 +165,10 @@ class UnfoldTransitionModule {
@IntoMap
@ClassKey(UnfoldTraceLogger::class)
fun bindUnfoldTraceLogger(impl: UnfoldTraceLogger): CoreStartable
+
+ @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository
+
+ @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
new file mode 100644
index 000000000000..0d3682c9a24b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.unfold.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+/** Repository for fold/unfold transitions */
+interface UnfoldTransitionRepository {
+ /** Returns false if fold/unfold transitions are not available on this device */
+ val isAvailable: Boolean
+
+ /**
+ * Emits current transition state on each transition change such as transition start or finish
+ * [UnfoldTransitionStatus]
+ */
+ val transitionStatus: Flow<UnfoldTransitionStatus>
+}
+
+/** Transition event of fold/unfold transition */
+sealed class UnfoldTransitionStatus {
+ /** Status that is sent when fold or unfold transition is in started state */
+ data object TransitionStarted : UnfoldTransitionStatus()
+ /** Status that is sent when fold or unfold transition is finished */
+ data object TransitionFinished : UnfoldTransitionStatus()
+}
+
+class UnfoldTransitionRepositoryImpl
+@Inject
+constructor(
+ private val unfoldProgressProvider: Optional<UnfoldTransitionProgressProvider>,
+) : UnfoldTransitionRepository {
+
+ override val isAvailable: Boolean
+ get() = unfoldProgressProvider.isPresent
+
+ override val transitionStatus: Flow<UnfoldTransitionStatus>
+ get() {
+ val provider = unfoldProgressProvider.getOrNull() ?: return emptyFlow()
+
+ return conflatedCallbackFlow {
+ val callback =
+ object : UnfoldTransitionProgressProvider.TransitionProgressListener {
+ override fun onTransitionStarted() {
+ trySend(TransitionStarted)
+ }
+
+ override fun onTransitionFinished() {
+ trySend(TransitionFinished)
+ }
+ }
+ provider.addCallback(callback)
+ awaitClose { provider.removeCallback(callback) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
new file mode 100644
index 000000000000..a2e77afedea6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.unfold.domain.interactor
+
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+
+interface UnfoldTransitionInteractor {
+ val isAvailable: Boolean
+
+ suspend fun waitForTransitionFinish()
+}
+
+class UnfoldTransitionInteractorImpl
+@Inject
+constructor(private val repository: UnfoldTransitionRepository) : UnfoldTransitionInteractor {
+
+ override val isAvailable: Boolean
+ get() = repository.isAvailable
+
+ override suspend fun waitForTransitionFinish() {
+ repository.transitionStatus.filter { it is TransitionFinished }.first()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/EventLog.kt b/packages/SystemUI/src/com/android/systemui/util/EventLog.kt
new file mode 100644
index 000000000000..dc794cf66a8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EventLog.kt
@@ -0,0 +1,43 @@
+/*
+ * 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
+
+/**
+ * Testable wrapper around {@link android.util.EventLog}.
+ *
+ * Dagger can inject this wrapper into your classes. The implementation just proxies calls to the
+ * real EventLog.
+ *
+ * In tests, pass an instance of FakeEventLog, which allows you to examine the values passed to the
+ * various methods below.
+ */
+interface EventLog {
+ /** @see android.util.EventLog.writeEvent */
+ fun writeEvent(tag: Int, value: Int): Int
+
+ /** @see android.util.EventLog.writeEvent */
+ fun writeEvent(tag: Int, value: Long): Int
+
+ /** @see android.util.EventLog.writeEvent */
+ fun writeEvent(tag: Int, value: Float): Int
+
+ /** @see android.util.EventLog.writeEvent */
+ fun writeEvent(tag: Int, value: String): Int
+
+ /** @see android.util.EventLog.writeEvent */
+ fun writeEvent(tag: Int, vararg values: Any): Int
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/EventLogImpl.kt b/packages/SystemUI/src/com/android/systemui/util/EventLogImpl.kt
new file mode 100644
index 000000000000..6fb1adc9382d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EventLogImpl.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.util
+
+import javax.inject.Inject
+
+/** Default implementation of [com.android.systemui.util.EventLog]. */
+class EventLogImpl @Inject constructor() : EventLog {
+ override fun writeEvent(tag: Int, value: Int): Int =
+ android.util.EventLog.writeEvent(tag, value)
+
+ override fun writeEvent(tag: Int, value: Long): Int =
+ android.util.EventLog.writeEvent(tag, value)
+
+ override fun writeEvent(tag: Int, value: Float): Int =
+ android.util.EventLog.writeEvent(tag, value)
+
+ override fun writeEvent(tag: Int, value: String): Int =
+ android.util.EventLog.writeEvent(tag, value)
+
+ override fun writeEvent(tag: Int, vararg values: Any): Int =
+ android.util.EventLog.writeEvent(tag, *values)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt b/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt
new file mode 100644
index 000000000000..ca0876ca32cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EventLogModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface EventLogModule {
+ @SysUISingleton @Binds fun bindEventLog(eventLogImpl: EventLogImpl?): EventLog?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index db4ab7edbcf1..5b9161593703 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -25,6 +25,16 @@ import com.android.app.animation.Interpolators
* The fraction after which we start fading in when going from a gone widget to a visible one
*/
private const val GONE_FADE_FRACTION = 0.8f
+/**
+ * The fraction after which we start fading in going from a gone widget to a visible one in guts
+ * animation.
+ */
+private const val GONE_FADE_GUTS_FRACTION = 0.286f
+/**
+ * The fraction before which we fade out when going from a visible widget to a gone one in guts
+ * animation.
+ */
+private const val VISIBLE_FADE_GUTS_FRACTION = 0.355f
/**
* The amont we're scaling appearing views
@@ -45,6 +55,7 @@ open class TransitionLayoutController {
private var animationStartState: TransitionViewState? = null
private var state = TransitionViewState()
private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
+ private var isGutsAnimation = false
private var currentHeight: Int = 0
private var currentWidth: Int = 0
var sizeChangedListener: ((Int, Int) -> Unit)? = null
@@ -152,15 +163,6 @@ open class TransitionLayoutController {
// this looks quite ugly
val nowGone: Boolean
if (widgetStart.gone) {
-
- // Only fade it in at the very end
- alphaProgress = MathUtils.map(GONE_FADE_FRACTION, 1.0f, 0.0f, 1.0f, progress)
- nowGone = progress < GONE_FADE_FRACTION
-
- // Scale it just a little, not all the way
- val endScale = widgetEnd.scale
- newScale = MathUtils.lerp(GONE_SCALE_AMOUNT * endScale, endScale, progress)
-
// don't clip
widthProgress = 1.0f
@@ -168,25 +170,52 @@ open class TransitionLayoutController {
resultMeasureWidth = widgetEnd.measureWidth
resultMeasureHeight = widgetEnd.measureHeight
- // Let's make sure we're centering the view in the gone view instead of having
- // the left at 0
- resultX = MathUtils.lerp(widgetStart.x - resultMeasureWidth / 2.0f,
- widgetEnd.x,
- progress)
- resultY = MathUtils.lerp(widgetStart.y - resultMeasureHeight / 2.0f,
- widgetEnd.y,
- progress)
+ if (isGutsAnimation) {
+ // if animation is open/close guts, fade in starts early.
+ alphaProgress = MathUtils.map(
+ GONE_FADE_GUTS_FRACTION,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress < GONE_FADE_GUTS_FRACTION
+
+ // Do not change scale of widget.
+ newScale = 1.0f
+
+ // We do not want any horizontal or vertical movement.
+ resultX = widgetStart.x
+ resultY = widgetStart.y
+ } else {
+ // Only fade it in at the very end
+ alphaProgress = MathUtils.map(
+ GONE_FADE_FRACTION,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress < GONE_FADE_FRACTION
+
+ // Scale it just a little, not all the way
+ val endScale = widgetEnd.scale
+ newScale = MathUtils.lerp(GONE_SCALE_AMOUNT * endScale, endScale, progress)
+
+ // Let's make sure we're centering the view in the gone view instead of
+ // having the left at 0
+ resultX = MathUtils.lerp(
+ widgetStart.x - resultMeasureWidth / 2.0f,
+ widgetEnd.x,
+ progress
+ )
+ resultY = MathUtils.lerp(
+ widgetStart.y - resultMeasureHeight / 2.0f,
+ widgetEnd.y,
+ progress
+ )
+ }
} else {
-
- // Fadeout in the very beginning
- alphaProgress = MathUtils.map(0.0f, 1.0f - GONE_FADE_FRACTION, 0.0f, 1.0f,
- progress)
- nowGone = progress > 1.0f - GONE_FADE_FRACTION
-
- // Scale it just a little, not all the way
- val startScale = widgetStart.scale
- newScale = MathUtils.lerp(startScale, startScale * GONE_SCALE_AMOUNT, progress)
-
// Don't clip
widthProgress = 0.0f
@@ -194,14 +223,54 @@ open class TransitionLayoutController {
resultMeasureWidth = widgetStart.measureWidth
resultMeasureHeight = widgetStart.measureHeight
- // Let's make sure we're centering the view in the gone view instead of having
- // the left at 0
- resultX = MathUtils.lerp(widgetStart.x,
- widgetEnd.x - resultMeasureWidth / 2.0f,
- progress)
- resultY = MathUtils.lerp(widgetStart.y,
- widgetEnd.y - resultMeasureHeight / 2.0f,
- progress)
+ // Fadeout in the very beginning
+ if (isGutsAnimation) {
+ alphaProgress = MathUtils.map(
+ 0.0f,
+ VISIBLE_FADE_GUTS_FRACTION,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress > VISIBLE_FADE_GUTS_FRACTION
+
+ // Do not change scale of widget during open/close guts animation.
+ newScale = 1.0f
+
+ // We do not want any horizontal or vertical movement.
+ resultX = widgetEnd.x
+ resultY = widgetEnd.y
+ } else {
+ alphaProgress = MathUtils.map(
+ 0.0f,
+ 1.0f - GONE_FADE_FRACTION,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress > 1.0f - GONE_FADE_FRACTION
+
+ // Scale it just a little, not all the way
+ val startScale = widgetStart.scale
+ newScale = MathUtils.lerp(
+ startScale,
+ startScale * GONE_SCALE_AMOUNT,
+ progress
+ )
+
+ // Let's make sure we're centering the view in the gone view instead of
+ // having the left at 0
+ resultX = MathUtils.lerp(
+ widgetStart.x,
+ widgetEnd.x - resultMeasureWidth / 2.0f,
+ progress
+ )
+ resultY = MathUtils.lerp(
+ widgetStart.y,
+ widgetEnd.y - resultMeasureHeight / 2.0f,
+ progress
+ )
+ }
}
resultWidgetState.gone = nowGone
} else {
@@ -279,8 +348,10 @@ open class TransitionLayoutController {
applyImmediately: Boolean,
animate: Boolean,
duration: Long = 0,
- delay: Long = 0
+ delay: Long = 0,
+ isGuts: Boolean,
) {
+ isGutsAnimation = isGuts
val animated = animate && currentState.width != 0
this.state = state.copy()
if (applyImmediately || transitionLayout == null) {
@@ -291,6 +362,8 @@ open class TransitionLayoutController {
animationStartState = currentState.copy()
animator.duration = duration
animator.startDelay = delay
+ animator.interpolator =
+ if (isGutsAnimation) Interpolators.LINEAR else Interpolators.FAST_OUT_SLOW_IN
animator.start()
} else if (!animator.isRunning) {
applyStateToLayout(this.state)
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt
new file mode 100644
index 000000000000..adae782eeb98
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.animation.data.repository
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.withContext
+
+/** Utility class that could give information about if animation are enabled in the system */
+interface AnimationStatusRepository {
+ fun areAnimationsEnabled(): Flow<Boolean>
+}
+
+class AnimationStatusRepositoryImpl
+@Inject
+constructor(
+ private val resolver: ContentResolver,
+ @Background private val backgroundHandler: Handler,
+ @Background private val backgroundDispatcher: CoroutineDispatcher
+) : AnimationStatusRepository {
+
+ /**
+ * Emits true if animations are enabled in the system, after subscribing it immediately emits
+ * the current state
+ */
+ override fun areAnimationsEnabled(): Flow<Boolean> = conflatedCallbackFlow {
+ val initialValue = withContext(backgroundDispatcher) { resolver.areAnimationsEnabled() }
+ trySend(initialValue)
+
+ val observer =
+ object : ContentObserver(backgroundHandler) {
+ override fun onChange(selfChange: Boolean) {
+ val updatedValue = resolver.areAnimationsEnabled()
+ trySend(updatedValue)
+ }
+ }
+
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ /* notifyForDescendants= */ false,
+ observer
+ )
+
+ awaitClose { resolver.unregisterContentObserver(observer) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 981bf01164e3..9c8a481fcb76 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,6 +18,8 @@ package com.android.systemui.util.dagger;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepository;
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepositoryImpl;
import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
@@ -31,4 +33,8 @@ public interface UtilModule {
/** */
@Binds
RingerModeTracker provideRingerModeTracker(RingerModeTrackerImpl ringerModeTrackerImpl);
+
+ @Binds
+ AnimationStatusRepository provideAnimationStatus(
+ AnimationStatusRepositoryImpl ringerModeTrackerImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
new file mode 100644
index 000000000000..a2a44e46919f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.drawable
+
+import android.content.res.Resources
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+import androidx.appcompat.graphics.drawable.DrawableWrapperCompat
+
+/**
+ * Create a looped [Animatable2] restarting it when the animation finishes on its own. Calling
+ * [LoopedAnimatable2DrawableWrapper.stop] cancels further looping.
+ */
+class LoopedAnimatable2DrawableWrapper private constructor(private val animatable2: Animatable2) :
+ DrawableWrapperCompat(animatable2 as Drawable), Animatable2 {
+
+ private val loopedCallback = LoopedCallback()
+
+ override fun start() {
+ animatable2.start()
+ animatable2.registerAnimationCallback(loopedCallback)
+ }
+
+ override fun stop() {
+ // stop looping if someone stops the animation
+ animatable2.unregisterAnimationCallback(loopedCallback)
+ animatable2.stop()
+ }
+
+ override fun isRunning(): Boolean = animatable2.isRunning
+
+ override fun registerAnimationCallback(callback: Animatable2.AnimationCallback) =
+ animatable2.registerAnimationCallback(callback)
+
+ override fun unregisterAnimationCallback(callback: Animatable2.AnimationCallback): Boolean =
+ animatable2.unregisterAnimationCallback(callback)
+
+ override fun clearAnimationCallbacks() = animatable2.clearAnimationCallbacks()
+
+ override fun getConstantState(): ConstantState? =
+ drawable!!.constantState?.let(LoopedAnimatable2DrawableWrapper::LoopedDrawableState)
+
+ companion object {
+
+ /**
+ * Creates [LoopedAnimatable2DrawableWrapper] from a [drawable]. The [drawable] should
+ * implement [Animatable2].
+ *
+ * It supports the following resource tags:
+ * - `<animated-image>`
+ * - `<animated-vector>`
+ */
+ fun fromDrawable(drawable: Drawable): LoopedAnimatable2DrawableWrapper {
+ require(drawable is Animatable2)
+ return LoopedAnimatable2DrawableWrapper(drawable)
+ }
+ }
+
+ private class LoopedCallback : Animatable2.AnimationCallback() {
+
+ override fun onAnimationEnd(drawable: Drawable?) {
+ (drawable as? Animatable2)?.start()
+ }
+ }
+
+ private class LoopedDrawableState(private val nestedState: ConstantState) : ConstantState() {
+
+ override fun newDrawable(): Drawable = fromDrawable(nestedState.newDrawable())
+
+ override fun newDrawable(res: Resources?): Drawable =
+ fromDrawable(nestedState.newDrawable(res))
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable =
+ fromDrawable(nestedState.newDrawable(res, theme))
+
+ override fun canApplyTheme(): Boolean = nestedState.canApplyTheme()
+
+ override fun getChangingConfigurations(): Int = nestedState.changingConfigurations
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index 81737c79905e..cc9335edfc14 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -5,8 +5,7 @@ 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.dagger.qualifiers.Tracing
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.Flags.coroutineTracing
import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
import com.android.app.tracing.TraceContextElement
import dagger.Module
@@ -15,32 +14,9 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import javax.inject.Qualifier
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
-/** Key associated with a [Boolean] flag that enables or disables the coroutine tracing feature. */
-@Qualifier
-annotation class CoroutineTracingEnabledKey
-
-/**
- * Same as [@Application], but does not make use of flags. This should only be used when early usage
- * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic].
- */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnflaggedApplication
-
-/**
- * Same as [@Background], but does not make use of flags. This should only be used when early usage
- * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic].
- */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnflaggedBackground
-
/** Providers for various coroutines-related constructs. */
@Module
class CoroutinesModule {
@@ -53,11 +29,6 @@ class CoroutinesModule {
@Provides
@SysUISingleton
- @UnflaggedApplication
- fun unflaggedApplicationScope(): CoroutineScope = CoroutineScope(Dispatchers.Main.immediate)
-
- @Provides
- @SysUISingleton
@Main
@Deprecated(
"Use @Main CoroutineContext instead",
@@ -98,28 +69,14 @@ class CoroutinesModule {
return Dispatchers.IO + tracingCoroutineContext
}
- @Provides
- @UnflaggedBackground
- @SysUISingleton
- fun unflaggedBackgroundCoroutineContext(): CoroutineContext {
- return Dispatchers.IO
- }
-
@OptIn(ExperimentalCoroutinesApi::class)
@Provides
@Tracing
@SysUISingleton
- fun tracingCoroutineContext(
- @CoroutineTracingEnabledKey enableTracing: Boolean
- ): CoroutineContext = if (enableTracing) TraceContextElement() else EmptyCoroutineContext
-
- companion object {
- @[Provides CoroutineTracingEnabledKey]
- fun provideIsCoroutineTracingEnabledKey(featureFlags: FeatureFlagsClassic): Boolean {
- return if (featureFlags.isEnabled(Flags.COROUTINE_TRACING)) {
- coroutineTracingIsEnabled = true
- true
- } else false
- }
+ fun tracingCoroutineContext(): CoroutineContext {
+ return if (coroutineTracing()) {
+ coroutineTracingIsEnabled = true
+ TraceContextElement()
+ } else EmptyCoroutineContext
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Rect.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Rect.kt
new file mode 100644
index 000000000000..bcbc89c3ccd7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Rect.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.kotlin
+
+import android.graphics.Rect
+
+/** Returns the area of this rectangle */
+val Rect.area: Long
+ get() = width().toLong() * height().toLong()
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 ffbc10aa5f59..2336a8e46f07 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -16,6 +16,9 @@
package com.android.systemui.util.kotlin
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
class Utils {
companion object {
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
@@ -27,6 +30,45 @@ class Utils {
fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
+
+ /**
+ * 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>) -> (A, B, C)
+ */
+ fun <A, B, C> Flow<A>.sample(b: Flow<B>, c: Flow<C>): Flow<Triple<A, B, C>> {
+ return this.sample(combine(b, c, ::Pair), ::toTriple)
+ }
+
+ /**
+ * 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>) -> (A, B, C, D)
+ */
+ fun <A, B, C, D> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>
+ ): Flow<Quad<A, B, C, D>> {
+ return this.sample(combine(b, c, d, ::Triple), ::toQuad)
+ }
+
+ /**
+ * 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>) -> (A, B, C, D, E)
+ */
+ fun <A, B, C, D, E> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>,
+ e: Flow<E>,
+ ): Flow<Quint<A, B, C, D, E>> {
+ return this.sample(combine(b, c, d, e, ::Quad), ::toQuint)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
index eb0bf46159dd..d6e6f3fc56b1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -35,8 +35,8 @@ import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -46,7 +46,8 @@ import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
/**
- * A class that implements the four Computed Sound Dose-related warnings defined in {@link AudioManager}:
+ * A class that implements the three Computed Sound Dose-related warnings defined in
+ * {@link AudioManager}:
* <ul>
* <li>{@link AudioManager#CSD_WARNING_DOSE_REACHED_1X}</li>
* <li>{@link AudioManager#CSD_WARNING_DOSE_REPEATED_5X}</li>
@@ -188,8 +189,8 @@ public class CsdWarningDialog extends SystemUIDialog
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
Log.d(TAG, "Lower volume pressed for CSD warning " + mCsdWarning);
+ mAudioManager.lowerVolumeToRs1();
dismiss();
-
}
if (D.BUG) Log.d(TAG, "on click " + which);
}
@@ -216,10 +217,6 @@ public class CsdWarningDialog extends SystemUIDialog
@Override
public void onDismiss(DialogInterface unused) {
- if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REPEATED_5X) {
- // level is always reduced to RS1 beyond the 5x dose
- mAudioManager.lowerVolumeToRs1();
- }
try {
mContext.unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 84d2b3761fcc..404621d1fe81 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -34,7 +34,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -83,7 +82,6 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -120,7 +118,6 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -304,7 +301,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private final DevicePostureController mDevicePostureController;
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
- private final FeatureFlags mFeatureFlags;
private final Lazy<SecureSettings> mSecureSettings;
private int mDialogTimeoutMillis;
@@ -323,9 +319,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
- mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mHandler = new H(looper);
@@ -1373,14 +1367,12 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private void provideTouchFeedbackH(int newRingerMode) {
VibrationEffect effect = null;
- int hapticConstant = HapticFeedbackConstants.NO_HAPTICS;
switch (newRingerMode) {
case RINGER_MODE_NORMAL:
mController.scheduleTouchFeedback();
break;
case RINGER_MODE_SILENT:
effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_OFF;
break;
case RINGER_MODE_VIBRATE:
// Feedback handled by onStateChange, for feedback both when user toggles
@@ -1388,11 +1380,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
break;
default:
effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_ON;
}
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mDialogView.performHapticFeedback(hapticConstant);
- } else if (effect != null) {
+ if (effect != null) {
mController.vibrate(effect);
}
}
@@ -1820,22 +1809,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
&& mState.ringerModeInternal != -1
&& mState.ringerModeInternal != state.ringerModeInternal
&& state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
-
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- if (mShowing) {
- // The dialog view is responsible for triggering haptics in the oneway API
- mDialogView.performHapticFeedback(HapticFeedbackConstants.TOGGLE_ON);
- }
- /*
- TODO(b/290642122): If the dialog is not showing, we have the case where haptics is
- enabled by dragging the volume slider of Settings to a value of 0. This must be
- handled by view Slices in Settings whilst using the performHapticFeedback API.
- */
-
- } else {
- // Old behavior only active if the oneway API is not used.
- mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
- }
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
mState = state;
mDynamic.clear();
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 e3b3c21d5d0d..53217d481599 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -22,7 +22,6 @@ import android.os.Looper;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -65,7 +64,6 @@ public interface VolumeModule {
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
@@ -82,7 +80,6 @@ public interface VolumeModule {
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags,
secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 4d8768f5e9e0..2e9b7e84344b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -127,7 +127,6 @@ class ClockEventControllerTest : SysuiTestCase() {
withDeps.featureFlags.apply {
set(Flags.REGION_SAMPLING, false)
- set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, false)
set(Flags.FACE_AUTH_REFACTOR, false)
}
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index a38ba00d898f..7feab9141da2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -21,7 +21,6 @@ import static android.view.View.INVISIBLE;
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.flags.Flags.MIGRATE_CLOCKS_TO_BLUEPRINT;
-import static com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_VIEW;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeast;
@@ -60,6 +59,7 @@ import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -181,7 +181,6 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
mFakeFeatureFlags = new FakeFeatureFlags();
mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false);
mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
- mFakeFeatureFlags.set(MIGRATE_KEYGUARD_STATUS_VIEW, false);
mFakeFeatureFlags.set(MIGRATE_CLOCKS_TO_BLUEPRINT, false);
mController = new KeyguardClockSwitchController(
mView,
@@ -192,9 +191,11 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
mSmartspaceController,
mock(ConfigurationController.class),
mock(ScreenOffAnimationController.class),
+ mock(StatusBarIconViewBindingFailureTracker.class),
mKeyguardUnlockAnimationController,
mSecureSettings,
mExecutor,
+ mExecutor,
mDumpManager,
mClockEventController,
mLogBuffer,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index d84c2c0415eb..cb26e6193ae6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -157,6 +157,7 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
+ mExecutor.runAllReady();
verify(mSecureSettings).registerContentObserverForUser(
eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
@@ -212,6 +213,7 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
+ mExecutor.runAllReady();
verify(mSecureSettings).registerContentObserverForUser(
eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
observerCaptor.capture(), eq(UserHandle.USER_ALL));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 22c75d85d4a1..146715d26b7d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -30,7 +30,6 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -60,7 +59,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
@Mock protected ScreenOffAnimationController mScreenOffAnimationController;
@Mock protected KeyguardLogger mKeyguardLogger;
@Mock protected KeyguardStatusViewController mControllerMock;
- @Mock protected FeatureFlags mFeatureFlags;
@Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ViewTreeObserver mViewTreeObserver;
@Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -91,7 +89,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
mDozeParameters,
mScreenOffAnimationController,
mKeyguardLogger,
- mFeatureFlags,
mInteractionJankMonitor,
deps.getKeyguardInteractor(),
mKeyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 776799ec054e..2b41e08065d1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -3436,7 +3436,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
- when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1});
+ when(mUsbPortStatus.getComplianceWarnings())
+ .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
}
private Context getSpyContext() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 1d4f2cbe6b64..d2f45ae8685a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -42,8 +42,8 @@ import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -78,7 +78,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
protected MockitoSession mStaticMockSession;
protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
- protected @Mock BouncerInteractor mBouncerInteractor;
+ protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
protected @Mock LockIconView mLockIconView;
protected @Mock AnimatedStateListDrawable mIconDrawable;
protected @Mock Context mContext;
@@ -176,7 +176,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags,
mPrimaryBouncerInteractor,
mContext,
- () -> mBouncerInteractor,
+ () -> mDeviceEntryInteractor,
mSceneTestUtils.getSceneContainerFlags()
);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index adcec10f9172..93a5393b41cf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -381,7 +381,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest {
// THEN show primary bouncer via keyguard view controller, not scene container
verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
- verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
}
@Test
@@ -395,7 +395,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest {
// THEN show primary bouncer
verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- verify(mBouncerInteractor).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor).attemptDeviceEntry();
}
@Test
@@ -408,6 +408,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest {
mUnderTest.onLongPress();
// THEN don't show primary bouncer
- verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
+ verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 67d6aa8e98cf..d8799e16ebdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -54,7 +54,7 @@ import org.mockito.MockitoAnnotations;
/**
* Tests for {@link android.view.accessibility.IWindowMagnificationConnection} retrieved from
- * {@link WindowMagnification}
+ * {@link Magnification}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -86,7 +86,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
private AccessibilityLogger mA11yLogger;
private IWindowMagnificationConnection mIWindowMagnificationConnection;
- private WindowMagnification mWindowMagnification;
+ private Magnification mMagnification;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Before
@@ -98,16 +98,16 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
return null;
}).when(mAccessibilityManager).setWindowMagnificationConnection(
any(IWindowMagnificationConnection.class));
- mWindowMagnification = new WindowMagnification(getContext(),
+ mMagnification = new Magnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
- mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+ mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class));
- mWindowMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
+ mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class));
- mWindowMagnification.requestWindowMagnificationConnection(true);
+ mMagnification.requestWindowMagnificationConnection(true);
assertNotNull(mIWindowMagnificationConnection);
mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback);
}
@@ -161,7 +161,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void showMagnificationButton() throws RemoteException {
// magnification settings panel should not be showing
- assertFalse(mWindowMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+ assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
mIWindowMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -195,8 +195,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
testUserId, TEST_DISPLAY, testScale);
waitForIdleSync();
- assertTrue(mWindowMagnification.mUsersScales.contains(testUserId));
- assertEquals(mWindowMagnification.mUsersScales.get(testUserId).get(TEST_DISPLAY),
+ assertTrue(mMagnification.mUsersScales.contains(testUserId));
+ assertEquals(mMagnification.mUsersScales.get(testUserId).get(TEST_DISPLAY),
(Float) testScale);
verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index d7b6602c2f5c..c972febf2c7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -65,7 +65,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class WindowMagnificationTest extends SysuiTestCase {
+public class MagnificationTest extends SysuiTestCase {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@Mock
@@ -82,7 +82,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
private CommandQueue mCommandQueue;
- private WindowMagnification mWindowMagnification;
+ private Magnification mMagnification;
private OverviewProxyListener mOverviewProxyListener;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@@ -107,12 +107,12 @@ public class WindowMagnificationTest extends SysuiTestCase {
when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
doAnswer(invocation -> {
- mWindowMagnification.mMagnificationSettingsControllerCallback
+ mMagnification.mMagnificationSettingsControllerCallback
.onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ true);
return null;
}).when(mMagnificationSettingsController).toggleSettingsPanelVisibility();
doAnswer(invocation -> {
- mWindowMagnification.mMagnificationSettingsControllerCallback
+ mMagnification.mMagnificationSettingsControllerCallback
.onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ false);
return null;
}).when(mMagnificationSettingsController).closeMagnificationSettings();
@@ -120,15 +120,15 @@ public class WindowMagnificationTest extends SysuiTestCase {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
- mWindowMagnification = new WindowMagnification(getContext(),
+ mMagnification = new Magnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
getContext().getSystemService(DisplayManager.class), mA11yLogger);
- mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+ mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
- mWindowMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
+ mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController);
- mWindowMagnification.start();
+ mMagnification.start();
final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(OverviewProxyListener.class);
@@ -156,7 +156,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mWindowMagnifierCallback
+ mMagnification.mWindowMagnifierCallback
.onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
@@ -169,7 +169,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mWindowMagnifierCallback
+ mMagnification.mWindowMagnifierCallback
.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
verify(mConnectionCallback).onPerformScaleAction(
@@ -181,7 +181,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mWindowMagnifierCallback
+ mMagnification.mWindowMagnifierCallback
.onAccessibilityActionPerformed(TEST_DISPLAY);
verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY);
@@ -192,14 +192,14 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mWindowMagnifierCallback.onMove(TEST_DISPLAY);
+ mMagnification.mWindowMagnifierCallback.onMove(TEST_DISPLAY);
verify(mConnectionCallback).onMove(TEST_DISPLAY);
}
@Test
public void onClickSettingsButton_enabled_showPanelForWindowMode() {
- mWindowMagnification.mWindowMagnifierCallback.onClickSettingsButton(TEST_DISPLAY);
+ mMagnification.mWindowMagnifierCallback.onClickSettingsButton(TEST_DISPLAY);
waitForIdleSync();
verify(mMagnificationSettingsController).toggleSettingsPanelVisibility();
@@ -212,7 +212,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
@Test
public void onSetMagnifierSize_delegateToMagnifier() {
final @MagnificationSize int index = MagnificationSize.SMALL;
- mWindowMagnification.mMagnificationSettingsControllerCallback.onSetMagnifierSize(
+ mMagnification.mMagnificationSettingsControllerCallback.onSetMagnifierSize(
TEST_DISPLAY, index);
waitForIdleSync();
@@ -225,7 +225,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
@Test
public void onSetDiagonalScrolling_delegateToMagnifier() {
- mWindowMagnification.mMagnificationSettingsControllerCallback.onSetDiagonalScrolling(
+ mMagnification.mMagnificationSettingsControllerCallback.onSetDiagonalScrolling(
TEST_DISPLAY, /* enable= */ true);
waitForIdleSync();
@@ -235,7 +235,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
@Test
public void onEditMagnifierSizeMode_windowActivated_delegateToMagnifier() {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
- mWindowMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode(
+ mMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode(
TEST_DISPLAY, /* enable= */ true);
waitForIdleSync();
@@ -243,7 +243,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
verify(mA11yLogger).log(
eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED));
- mWindowMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode(
+ mMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode(
TEST_DISPLAY, /* enable= */ false);
waitForIdleSync();
verify(mA11yLogger).log(
@@ -258,7 +258,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
waitForIdleSync();
final float scale = 3.0f;
final boolean updatePersistence = false;
- mWindowMagnification.mMagnificationSettingsControllerCallback.onMagnifierScale(
+ mMagnification.mMagnificationSettingsControllerCallback.onMagnifierScale(
TEST_DISPLAY, scale, updatePersistence);
verify(mConnectionCallback).onPerformScaleAction(
@@ -274,7 +274,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
+ mMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
TEST_DISPLAY, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
waitForIdleSync();
@@ -292,7 +292,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
+ mMagnification.mMagnificationSettingsControllerCallback.onModeSwitch(
TEST_DISPLAY, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
waitForIdleSync();
@@ -305,7 +305,7 @@ public class WindowMagnificationTest extends SysuiTestCase {
public void onSettingsPanelVisibilityChanged_windowActivated_delegateToMagnifier() {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
final boolean shown = false;
- mWindowMagnification.mMagnificationSettingsControllerCallback
+ mMagnification.mMagnificationSettingsControllerCallback
.onSettingsPanelVisibilityChanged(TEST_DISPLAY, shown);
waitForIdleSync();
@@ -325,9 +325,9 @@ public class WindowMagnificationTest extends SysuiTestCase {
@Test
public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
final WindowMagnificationController mController = mock(WindowMagnificationController.class);
- mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+ mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mController);
- mWindowMagnification.mMagnificationControllerSupplier.get(TEST_DISPLAY);
+ mMagnification.mMagnificationControllerSupplier.get(TEST_DISPLAY);
mOverviewProxyListener.onConnectionChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
index 2b51ac5e3187..a7e7dd074a33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.animation
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.core.animation.doOnEnd
-import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.doOnEnd
@@ -31,7 +30,6 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
-@FlakyTest(bugId = 302149604)
class AnimatorTestRuleOrderTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 6ac84bc503a2..64ddbc7828ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -26,10 +26,13 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -51,10 +54,12 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
+ @Mock private lateinit var tableLogger: TableLogBuffer
private val testUtils = SceneTestUtils(this)
private val testScope = testUtils.testScope
private val userRepository = FakeUserRepository()
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var underTest: AuthenticationRepository
@@ -67,6 +72,8 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
userRepository.setUserInfos(USER_INFOS)
runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
whenever(getSecurityMode.apply(anyInt())).thenAnswer { currentSecurityMode }
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
underTest =
AuthenticationRepositoryImpl(
@@ -76,6 +83,7 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
userRepository = userRepository,
lockPatternUtils = lockPatternUtils,
broadcastDispatcher = fakeBroadcastDispatcher,
+ mobileConnectionsRepository = mobileConnectionsRepository,
)
}
@@ -97,15 +105,20 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
assertThat(underTest.getAuthenticationMethod())
.isEqualTo(AuthenticationMethodModel.None)
+
+ currentSecurityMode = KeyguardSecurityModel.SecurityMode.SimPin
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Sim)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Sim)
}
@Test
- fun isAutoConfirmEnabled() =
+ fun isAutoConfirmFeatureEnabled() =
testScope.runTest {
whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[0].id)).thenReturn(true)
whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[1].id)).thenReturn(false)
- val values by collectValues(underTest.isAutoConfirmEnabled)
+ val values by collectValues(underTest.isAutoConfirmFeatureEnabled)
assertThat(values.first()).isFalse()
assertThat(values.last()).isTrue()
@@ -127,6 +140,38 @@ class AuthenticationRepositoryTest : SysuiTestCase() {
assertThat(values.last()).isTrue()
}
+ @Test
+ fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() =
+ testScope.runTest {
+ val authenticationChallengeResults by
+ collectValues(underTest.authenticationChallengeResult)
+
+ runCurrent()
+ underTest.reportAuthenticationAttempt(true)
+ runCurrent()
+ underTest.reportAuthenticationAttempt(false)
+ runCurrent()
+ underTest.reportAuthenticationAttempt(true)
+
+ assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true))
+ }
+
+ @Test
+ fun isPinEnhancedPrivacyEnabled() =
+ testScope.runTest {
+ whenever(lockPatternUtils.isPinEnhancedPrivacyEnabled(USER_INFOS[0].id))
+ .thenReturn(false)
+ whenever(lockPatternUtils.isPinEnhancedPrivacyEnabled(USER_INFOS[1].id))
+ .thenReturn(true)
+
+ val values by collectValues(underTest.isPinEnhancedPrivacyEnabled)
+ assertThat(values.first()).isTrue()
+ assertThat(values.last()).isFalse()
+
+ userRepository.setSelectedUserInfo(USER_INFOS[1])
+ assertThat(values.last()).isTrue()
+ }
+
private fun setSecurityModeAndDispatchBroadcast(
securityMode: KeyguardSecurityModel.SecurityMode,
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 2f5f4606c5f0..56d3d260d196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -20,9 +20,8 @@ import android.app.admin.DevicePolicyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
@@ -51,33 +50,16 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Pin)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Pin)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Password)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password)
assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Password)
- }
-
- @Test
- fun authenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() =
- testScope.runTest {
- val authMethod by collectLastValue(underTest.authenticationMethod)
- runCurrent()
-
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.None
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
-
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Swipe)
+ .isEqualTo(AuthenticationMethodModel.Password)
}
@Test
@@ -86,23 +68,18 @@ class AuthenticationInteractorTest : SysuiTestCase() {
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.None
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(false)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.None)
+ .isEqualTo(AuthenticationMethodModel.None)
}
@Test
fun authenticate_withCorrectPin_returnsTrue() =
testScope.runTest {
val isThrottled by collectLastValue(underTest.isThrottled)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isThrottled).isFalse()
@@ -111,9 +88,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun authenticate_withIncorrectPin_returnsFalse() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
.isEqualTo(AuthenticationResult.FAILED)
}
@@ -121,9 +96,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test(expected = IllegalArgumentException::class)
fun authenticate_withEmptyPin_throwsException() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
underTest.authenticate(listOf())
}
@@ -132,7 +105,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val pin = List(16) { 9 }
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(pin)
}
@@ -148,9 +121,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
// If the policy changes, there is work to do in SysUI.
assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(List(17) { 9 }))
.isEqualTo(AuthenticationResult.FAILED)
}
@@ -160,7 +131,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val isThrottled by collectLastValue(underTest.isThrottled)
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList()))
@@ -172,7 +143,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun authenticate_withIncorrectPassword_returnsFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("alohomora".toList()))
@@ -183,7 +154,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun authenticate_withCorrectPattern_returnsTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pattern
+ AuthenticationMethodModel.Pattern
)
assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
@@ -194,7 +165,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun authenticate_withIncorrectPattern_returnsFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pattern
+ AuthenticationMethodModel.Pattern
)
assertThat(
@@ -211,13 +182,16 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
- fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() =
+ fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
val isThrottled by collectLastValue(underTest.isThrottled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply {
@@ -231,12 +205,15 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
- fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() =
+ fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalse() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 },
@@ -244,17 +221,18 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.FAILED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() =
+ fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalse() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN + listOf(7),
@@ -262,17 +240,18 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.FAILED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() =
+ fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrue() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN,
@@ -280,16 +259,38 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
+ }
+
+ @Test
+ fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNull() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isTrue()
+ val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+ utils.authenticationRepository.apply {
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
+ setThrottleDuration(42)
+ }
+
+ val authResult =
+ underTest.authenticate(
+ FakeAuthenticationRepository.DEFAULT_PIN,
+ tryAutoConfirm = true
+ )
+
+ assertThat(authResult).isEqualTo(AuthenticationResult.SKIPPED)
+ assertThat(isAutoConfirmEnabled).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(hintedPinLength).isNull()
}
@Test
- fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() =
+ fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNull() =
testScope.runTest {
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(false)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(false)
}
assertThat(
underTest.authenticate(
@@ -298,53 +299,38 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.SKIPPED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() =
+ fun tryAutoConfirm_withoutCorrectPassword_returnsNull() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
fun throttling() =
testScope.runTest {
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
val throttling by collectLastValue(underTest.throttling)
val isThrottled by collectLastValue(underTest.isThrottled)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
- assertThat(isUnlocked).isTrue()
- assertThat(isThrottled).isFalse()
- assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
-
- utils.deviceEntryRepository.setUnlocked(false)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
// Make many wrong attempts, but just shy of what's needed to get throttled:
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) {
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
// Make one more wrong attempt, leading to throttling:
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isTrue()
assertThat(throttling)
.isEqualTo(
@@ -358,7 +344,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
// Correct PIN, but throttled, so doesn't attempt it:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isTrue()
assertThat(throttling)
.isEqualTo(
@@ -391,7 +376,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
// Move the clock forward one more second, to completely finish the throttling period:
advanceTimeBy(1000)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling)
.isEqualTo(
@@ -405,7 +389,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
// Correct PIN and no longer throttled so unlocks successfully:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(isUnlocked).isTrue()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
@@ -415,8 +398,8 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(false)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(false)
}
assertThat(hintedPinLength).isNull()
@@ -427,13 +410,13 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) }
}
)
- setAutoConfirmEnabled(true)
+ setAutoConfirmFeatureEnabled(true)
}
assertThat(hintedPinLength).isNull()
@@ -444,8 +427,8 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) }
@@ -461,15 +444,33 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) }
}
)
- setAutoConfirmEnabled(true)
+ setAutoConfirmFeatureEnabled(true)
}
assertThat(hintedPinLength).isNull()
}
+
+ @Test
+ fun authenticate_withTooShortPassword() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ assertThat(
+ underTest.authenticate(
+ buildList {
+ repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
+ add("$time")
+ }
+ }
+ )
+ )
+ .isEqualTo(AuthenticationResult.SKIPPED)
+ }
}
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 2d95b09cbf0e..f4122d59cea1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -50,8 +50,6 @@ import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteracto
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.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
@@ -87,8 +85,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@JvmField @Rule
var mockitoRule = MockitoJUnit.rule()
- private val featureFlags = FakeFeatureFlags()
-
@Mock
lateinit var callback: AuthDialogCallback
@Mock
@@ -135,7 +131,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Before
fun setup() {
displayRepository = FakeDisplayRepository()
- featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false)
displayStateInteractor =
DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 11c5d3bb27b3..602f3dc29491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -475,6 +475,22 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
+ public void testOnAuthenticationFailedInvoked_whenBiometricReEnrollRequired() {
+ showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+ final int modality = BiometricAuthenticator.TYPE_FACE;
+ mAuthController.onBiometricError(modality,
+ BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ 0 /* vendorCode */);
+
+ verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(),
+ mMessageCaptor.capture());
+
+ assertThat(mModalityCaptor.getValue()).isEqualTo(modality);
+ assertThat(mMessageCaptor.getValue()).isEqualTo(mContext.getString(
+ R.string.face_recalibrate_notification_content));
+ }
+
+ @Test
public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused() {
testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected(
BiometricConstants.BIOMETRIC_PAUSED_REJECTED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index d2b81e06c0e5..00ea78f01fa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -17,14 +17,14 @@
package com.android.systemui.biometrics
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import dagger.BindsInstance
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index b4b02a2dfb93..a1b801cd3d3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -55,6 +55,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -151,9 +152,11 @@ class SideFpsControllerTest : SysuiTestCase() {
mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
+ FakeFingerprintPropertyRepository(),
FakeBiometricSettingsRepository(),
FakeSystemClock(),
mock(KeyguardUpdateMonitor::class.java),
+ testScope.backgroundScope,
)
displayStateInteractor =
DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index c5f16aa97f18..f5b6f14a627c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -36,14 +36,14 @@ import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -68,7 +68,6 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
private const val REQUEST_ID = 2L
@@ -108,14 +107,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
- @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
- @Mock private lateinit var udfpsUtils: UdfpsUtils
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
UdfpsKeyguardAccessibilityDelegate
- @Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -125,47 +122,52 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Before
fun setup() {
whenever(inflater.inflate(R.layout.udfps_view, null, false))
- .thenReturn(udfpsView)
+ .thenReturn(udfpsView)
whenever(inflater.inflate(R.layout.udfps_bp_view, null))
- .thenReturn(mock(UdfpsBpView::class.java))
+ .thenReturn(mock(UdfpsBpView::class.java))
whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
- .thenReturn(mUdfpsKeyguardViewLegacy)
+ .thenReturn(mUdfpsKeyguardViewLegacy)
whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
- .thenReturn(mock(UdfpsFpmEmptyView::class.java))
+ .thenReturn(mock(UdfpsFpmEmptyView::class.java))
}
private fun withReason(
- @ShowReason reason: Int,
- isDebuggable: Boolean = false,
- block: () -> Unit
+ @ShowReason reason: Int,
+ isDebuggable: Boolean = false,
+ enableDeviceEntryUdfpsRefactor: Boolean = false,
+ block: () -> Unit,
) {
+ if (enableDeviceEntryUdfpsRefactor) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ }
controllerOverlay = UdfpsControllerOverlay(
- context,
- inflater,
- windowManager,
- accessibilityManager,
- statusBarStateController,
- statusBarKeyguardViewManager,
- keyguardUpdateMonitor,
- dialogManager,
- dumpManager,
- transitionController,
- configurationController,
- keyguardStateController,
- unlockedScreenOffAnimationController,
- udfpsDisplayMode,
- REQUEST_ID,
- reason,
- controllerCallback,
- onTouch,
- activityLaunchAnimator,
- featureFlags,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- isDebuggable,
- udfpsKeyguardAccessibilityDelegate,
- udfpsKeyguardViewModels,
- mSelectedUserInteractor,
+ context,
+ inflater,
+ windowManager,
+ accessibilityManager,
+ statusBarStateController,
+ statusBarKeyguardViewManager,
+ keyguardUpdateMonitor,
+ dialogManager,
+ dumpManager,
+ transitionController,
+ configurationController,
+ keyguardStateController,
+ unlockedScreenOffAnimationController,
+ udfpsDisplayMode,
+ REQUEST_ID,
+ reason,
+ controllerCallback,
+ onTouch,
+ activityLaunchAnimator,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ isDebuggable,
+ udfpsKeyguardAccessibilityDelegate,
+ keyguardTransitionInteractor,
+ mSelectedUserInteractor,
)
block()
}
@@ -187,12 +189,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
overlayParams = UdfpsOverlayParams(
- sensorBounds,
- overlayBounds,
- DISPLAY_WIDTH,
- DISPLAY_HEIGHT,
- scaleFactor = 1f,
- rotation
+ sensorBounds,
+ overlayBounds,
+ DISPLAY_WIDTH,
+ DISPLAY_HEIGHT,
+ scaleFactor = 1f,
+ rotation
)
block()
}
@@ -202,8 +204,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
+ eq(controllerOverlay.getTouchOverlay()),
+ layoutParamsCaptor.capture()
)
// ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
@@ -220,8 +222,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
+ eq(controllerOverlay.getTouchOverlay()),
+ layoutParamsCaptor.capture()
)
// ROTATION_180 is not supported. Sensor should stay in the top left corner.
@@ -238,8 +240,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
+ eq(controllerOverlay.getTouchOverlay()),
+ layoutParamsCaptor.capture()
)
// Sensor should be in the bottom left corner in ROTATION_90.
@@ -256,8 +258,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
+ eq(controllerOverlay.getTouchOverlay()),
+ layoutParamsCaptor.capture()
)
// Sensor should be in the top right corner in ROTATION_270.
@@ -272,7 +274,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
private fun showUdfpsOverlay() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
+ verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any())
verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any())
@@ -280,7 +282,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
assertThat(didShow).isTrue()
assertThat(controllerOverlay.isShowing).isTrue()
assertThat(controllerOverlay.isHiding).isFalse()
- assertThat(controllerOverlay.overlayView).isNotNull()
+ assertThat(controllerOverlay.getTouchOverlay()).isNotNull()
}
@Test
@@ -297,14 +299,14 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
private fun hideUdfpsOverlay() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
- val view = controllerOverlay.overlayView
+ val view = controllerOverlay.getTouchOverlay()
val didHide = controllerOverlay.hide()
verify(windowManager).removeView(eq(view))
assertThat(didShow).isTrue()
assertThat(didHide).isTrue()
- assertThat(controllerOverlay.overlayView).isNull()
+ assertThat(controllerOverlay.getTouchOverlay()).isNull()
assertThat(controllerOverlay.animationViewController).isNull()
assertThat(controllerOverlay.isShowing).isFalse()
assertThat(controllerOverlay.isHiding).isTrue()
@@ -350,8 +352,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
+ eq(controllerOverlay.getTouchOverlay()),
+ layoutParamsCaptor.capture()
)
// Layout params should use sensor bounds
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 675ca639493e..e2cab29c473c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -73,6 +73,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -86,6 +87,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -237,6 +239,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private ViewRootImpl mViewRootImpl;
@Mock
private FpsUnlockTracker mFpsUnlockTracker;
+ @Mock
+ private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Before
public void setUp() {
@@ -283,6 +287,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// Create a fake background executor.
mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
initUdfpsController(mOpticalProps);
}
@@ -301,7 +306,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
- mFeatureFlags,
mFalsingManager,
mPowerManager,
mAccessibilityManager,
@@ -329,7 +333,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mUdfpsKeyguardAccessibilityDelegate,
mUdfpsKeyguardViewModels,
mSelectedUserInteractor,
- mFpsUnlockTracker
+ mFpsUnlockTracker,
+ mKeyguardTransitionInteractor
);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 2c4e1362bed3..2ea803c6aa8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -31,6 +31,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -71,6 +72,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
protected @Mock SelectedUserInteractor mSelectedUserInteractor;
+ protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -141,11 +143,11 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
mDialogManager,
mUdfpsController,
mActivityLaunchAnimator,
- mFeatureFlags,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
- mSelectedUserInteractor);
+ mSelectedUserInteractor,
+ mKeyguardTransitionInteractor);
return controller;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index 21928cd606ed..98d8b054716c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
@@ -66,17 +66,12 @@ public class UdfpsKeyguardViewLegacyControllerTest extends
public void testViewControllerQueriesSBStateOnAttached() {
mController.onViewAttached();
verify(mStatusBarStateController).getState();
- verify(mStatusBarStateController).getDozeAmount();
- final float dozeAmount = .88f;
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
- when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount);
captureStatusBarStateListeners();
mController.onViewAttached();
verify(mView, atLeast(1)).setPauseAuth(true);
- verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
}
@Test
@@ -91,19 +86,6 @@ public class UdfpsKeyguardViewLegacyControllerTest extends
}
@Test
- public void testDozeEventsSentToView() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- final float linear = .55f;
- final float eased = .65f;
- mStatusBarStateListener.onDozeAmountChanged(linear, eased);
-
- verify(mView).onDozeAmountChanged(linear, eased,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
- }
-
- @Test
public void testShouldPauseAuthUnpausedAlpha0() {
mController.onViewAttached();
captureStatusBarStateListeners();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 97dada27f8c0..9bff88bbf2dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -21,6 +21,7 @@ import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -31,7 +32,12 @@ import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
@@ -49,6 +55,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
@@ -65,6 +72,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
private val testScope = TestScope(testDispatcher)
private lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
@Mock private lateinit var bouncerLogger: TableLogBuffer
@@ -78,6 +86,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
testScope.backgroundScope,
bouncerLogger,
)
+ transitionRepository = FakeKeyguardTransitionRepository()
super.setUp()
}
@@ -103,10 +112,18 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
+ FakeFingerprintPropertyRepository(),
mock(BiometricSettingsRepository::class.java),
mock(SystemClock::class.java),
mKeyguardUpdateMonitor,
+ testScope.backgroundScope,
)
+ mKeyguardTransitionInteractor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = transitionRepository,
+ )
+ .keyguardTransitionInteractor
return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
}
@@ -258,4 +275,145 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
job.cancel()
}
+
+ @Test
+ fun aodToLockscreen_dozeAmountChanged() =
+ testScope.runTest {
+ // GIVEN view is attached
+ mController.onViewAttached()
+ Mockito.reset(mView)
+
+ val job = mController.listenForLockscreenAodTransitions(this)
+
+ // WHEN transitioning from lockscreen to aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = .3f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(.3f),
+ eq(.3f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(1f),
+ eq(1f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun lockscreenToAod_dozeAmountChanged() =
+ testScope.runTest {
+ // GIVEN view is attached
+ mController.onViewAttached()
+ Mockito.reset(mView)
+
+ val job = mController.listenForLockscreenAodTransitions(this)
+
+ // WHEN transitioning from lockscreen to aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = .3f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(.3f),
+ eq(.3f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(1f),
+ eq(1f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun goneToAod_dozeAmountChanged() =
+ testScope.runTest {
+ // GIVEN view is attached
+ mController.onViewAttached()
+ Mockito.reset(mView)
+
+ val job = mController.listenForGoneToAodTransition(this)
+
+ // WHEN transitioning from lockscreen to aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = .3f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(.3f),
+ eq(.3f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_UNLOCKED_SCREEN_OFF)
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ runCurrent()
+ // THEN doze amount is updated
+ verify(mView)
+ .onDozeAmountChanged(
+ eq(1f),
+ eq(1f),
+ eq(UdfpsKeyguardViewLegacy.ANIMATION_UNLOCKED_SCREEN_OFF)
+ )
+
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
new file mode 100644
index 000000000000..b391b5a45799
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.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.bouncer.data.repository
+
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
+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.flow.Flow
+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.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SimBouncerRepositoryTest : SysuiTestCase() {
+ @Mock lateinit var euiccManager: EuiccManager
+ @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ private val dispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+ private val fakeSubscriptionManagerProxy = FakeSubscriptionManagerProxy()
+ private val keyguardUpdateMonitorCallbacks = mutableListOf<KeyguardUpdateMonitorCallback>()
+
+ private lateinit var underTest: SimBouncerRepositoryImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(/* testClass = */ this)
+ whenever(keyguardUpdateMonitor.registerCallback(any())).thenAnswer {
+ val cb = it.arguments[0] as KeyguardUpdateMonitorCallback
+ keyguardUpdateMonitorCallbacks.add(cb)
+ }
+ whenever(keyguardUpdateMonitor.removeCallback(any())).thenAnswer {
+ keyguardUpdateMonitorCallbacks.remove(it.arguments[0])
+ }
+ underTest =
+ SimBouncerRepositoryImpl(
+ applicationScope = testScope.backgroundScope,
+ backgroundDispatcher = dispatcher,
+ resources = context.resources,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ subscriptionManager = fakeSubscriptionManagerProxy,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ euiccManager = euiccManager,
+ )
+ }
+
+ @Test
+ fun subscriptionId() =
+ testScope.runTest {
+ val subscriptionId =
+ emitSubscriptionIdAndCollectLastValue(underTest.subscriptionId, subId = 2)
+ assertThat(subscriptionId).isEqualTo(2)
+ }
+
+ @Test
+ fun activeSubscriptionInfo() =
+ testScope.runTest {
+ fakeSubscriptionManagerProxy.setActiveSubscriptionInfo(subId = 2)
+ val activeSubscriptionInfo =
+ emitSubscriptionIdAndCollectLastValue(underTest.activeSubscriptionInfo, subId = 2)
+
+ assertThat(activeSubscriptionInfo?.subscriptionId).isEqualTo(2)
+ }
+
+ @Test
+ fun isLockedEsim_initialValue_isNull() =
+ testScope.runTest {
+ val isLockedEsim by collectLastValue(underTest.isLockedEsim)
+ assertThat(isLockedEsim).isNull()
+ }
+
+ @Test
+ fun isLockedEsim() =
+ testScope.runTest {
+ whenever(euiccManager.isEnabled).thenReturn(true)
+ fakeSubscriptionManagerProxy.setActiveSubscriptionInfo(subId = 2, isEmbedded = true)
+ val isLockedEsim =
+ emitSubscriptionIdAndCollectLastValue(underTest.isLockedEsim, subId = 2)
+ assertThat(isLockedEsim).isTrue()
+ }
+
+ @Test
+ fun isLockedEsim_notEmbedded() =
+ testScope.runTest {
+ fakeSubscriptionManagerProxy.setActiveSubscriptionInfo(subId = 2, isEmbedded = false)
+ val isLockedEsim =
+ emitSubscriptionIdAndCollectLastValue(underTest.isLockedEsim, subId = 2)
+ assertThat(isLockedEsim).isFalse()
+ }
+
+ @Test
+ fun isSimPukLocked() =
+ testScope.runTest {
+ val isSimPukLocked =
+ emitSubscriptionIdAndCollectLastValue(
+ underTest.isSimPukLocked,
+ subId = 2,
+ isSimPuk = true
+ )
+ assertThat(isSimPukLocked).isTrue()
+ }
+
+ @Test
+ fun setSimPukUserInput() {
+ val pukCode = "00000000"
+ val pinCode = "1234"
+ underTest.setSimPukUserInput(pukCode, pinCode)
+ assertThat(underTest.simPukInputModel.enteredSimPuk).isEqualTo(pukCode)
+ assertThat(underTest.simPukInputModel.enteredSimPin).isEqualTo(pinCode)
+ }
+
+ @Test
+ fun setSimPukUserInput_nullPuk() {
+ val pukCode = null
+ val pinCode = "1234"
+ underTest.setSimPukUserInput(pukCode, pinCode)
+ assertThat(underTest.simPukInputModel.enteredSimPuk).isNull()
+ assertThat(underTest.simPukInputModel.enteredSimPin).isEqualTo(pinCode)
+ }
+
+ @Test
+ fun setSimPukUserInput_nullPin() {
+ val pukCode = "00000000"
+ val pinCode = null
+ underTest.setSimPukUserInput(pukCode, pinCode)
+ assertThat(underTest.simPukInputModel.enteredSimPuk).isEqualTo(pukCode)
+ assertThat(underTest.simPukInputModel.enteredSimPin).isNull()
+ }
+
+ @Test
+ fun setSimPukUserInput_nullCodes() {
+ underTest.setSimPukUserInput()
+ assertThat(underTest.simPukInputModel.enteredSimPuk).isNull()
+ assertThat(underTest.simPukInputModel.enteredSimPin).isNull()
+ }
+
+ @Test
+ fun setSimPinVerificationErrorMessage() =
+ testScope.runTest {
+ val errorMsg = "error"
+ underTest.setSimVerificationErrorMessage(errorMsg)
+ val msg by collectLastValue(underTest.errorDialogMessage)
+ assertThat(msg).isEqualTo(errorMsg)
+ }
+
+ /** Emits a new sim card state and collects the last value of the flow argument. */
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun <T> TestScope.emitSubscriptionIdAndCollectLastValue(
+ flow: Flow<T>,
+ subId: Int = 1,
+ isSimPuk: Boolean = false
+ ): T? {
+ val value by collectLastValue(flow)
+ runCurrent()
+ val simState =
+ if (isSimPuk) {
+ TelephonyManager.SIM_STATE_PUK_REQUIRED
+ } else {
+ TelephonyManager.SIM_STATE_PIN_REQUIRED
+ }
+ whenever(keyguardUpdateMonitor.getNextSubIdForState(anyInt())).thenReturn(-1)
+ whenever(keyguardUpdateMonitor.getNextSubIdForState(simState)).thenReturn(subId)
+ keyguardUpdateMonitorCallbacks.forEach {
+ it.onSimStateChanged(subId, /* slotId= */ 0, simState)
+ }
+ runCurrent()
+ return value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 2d8adca04a5e..0d44ed30431f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -19,11 +19,13 @@ package com.android.systemui.bouncer.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -31,7 +33,7 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -47,8 +49,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
private lateinit var underTest: AlternateBouncerInteractor
private lateinit var bouncerRepository: KeyguardBouncerRepository
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private lateinit var deviceEntryFingerprintAuthRepository:
- FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var systemClock: SystemClock
@@ -61,19 +62,21 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
bouncerRepository =
KeyguardBouncerRepositoryImpl(
FakeSystemClock(),
- TestCoroutineScope(),
+ TestScope().backgroundScope,
bouncerLogger,
)
biometricSettingsRepository = FakeBiometricSettingsRepository()
- deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
underTest =
AlternateBouncerInteractor(
statusBarStateController,
keyguardStateController,
bouncerRepository,
+ fingerprintPropertyRepository,
biometricSettingsRepository,
systemClock,
keyguardUpdateMonitor,
+ TestScope().backgroundScope,
)
}
@@ -156,7 +159,17 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
+ fun canShowAlternateBouncerForFingerprint_rearFps() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ givenCanShowAlternateBouncer()
+ fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
+
+ assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+ }
+
+ @Test
fun alternateBouncerUiAvailable_fromMultipleSources() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
assertFalse(bouncerRepository.alternateBouncerUIAvailable.value)
// GIVEN there are two different sources indicating the alternate bouncer is available
@@ -178,7 +191,12 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
private fun givenCanShowAlternateBouncer() {
- bouncerRepository.setAlternateBouncerUIAvailable(true)
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ fingerprintPropertyRepository.supportsUdfps()
+ } else {
+ bouncerRepository.setAlternateBouncerUIAvailable(true)
+ }
+
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 915661b8a776..1e8073246f98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -19,16 +19,15 @@ package com.android.systemui.bouncer.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlin.math.ceil
import kotlin.time.Duration.Companion.milliseconds
@@ -39,51 +38,47 @@ 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
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class BouncerInteractorTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor
+
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
- private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
- private val underTest =
- utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
+
+ private lateinit var underTest: BouncerInteractor
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
overrideResource(R.string.keyguard_enter_your_pin, MESSAGE_ENTER_YOUR_PIN)
overrideResource(R.string.keyguard_enter_your_password, MESSAGE_ENTER_YOUR_PASSWORD)
overrideResource(R.string.keyguard_enter_your_pattern, MESSAGE_ENTER_YOUR_PATTERN)
overrideResource(R.string.kg_wrong_pin, MESSAGE_WRONG_PIN)
overrideResource(R.string.kg_wrong_password, MESSAGE_WRONG_PASSWORD)
overrideResource(R.string.kg_wrong_pattern, MESSAGE_WRONG_PATTERN)
+
+ underTest =
+ utils.bouncerInteractor(
+ authenticationInteractor = authenticationInteractor,
+ keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+ )
}
@Test
fun pinAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
-
underTest.clearMessage()
assertThat(message).isEmpty()
@@ -94,7 +89,6 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(underTest.authenticate(listOf(9, 8, 7)))
.isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -102,35 +96,38 @@ class BouncerInteractorTest : SysuiTestCase() {
// Correct input.
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
+ fun pinAuthMethod_sim_skipsAuthentication() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)
+ runCurrent()
+
+ // We rely on TelephonyManager to authenticate the sim card.
+ // Additionally, authenticating the sim card does not unlock the device.
+ // Thus, when auth method is sim, we expect to skip here.
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SKIPPED)
}
@Test
fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val message by collectLastValue(underTest.message)
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.authenticationRepository.setAutoConfirmEnabled(true)
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
- underTest.clearMessage()
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+ assertThat(isAutoConfirmEnabled).isTrue()
// Incomplete input.
assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Wrong 6-digit pin
assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.FAILED)
- assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Correct input.
assertThat(
@@ -140,27 +137,20 @@ class BouncerInteractorTest : SysuiTestCase() {
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.clearMessage()
// Incomplete input.
assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Correct input.
assertThat(
@@ -171,25 +161,16 @@ class BouncerInteractorTest : SysuiTestCase() {
)
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun passwordAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
-
- underTest.clearMessage()
- assertThat(message).isEmpty()
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
@@ -198,34 +179,36 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(underTest.authenticate("alohamora".toList()))
.isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
+ // Too short input.
+ assertThat(
+ underTest.authenticate(
+ buildList {
+ repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
+ add("$time")
+ }
+ }
+ )
+ )
+ .isEqualTo(AuthenticationResult.SKIPPED)
+ assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
+
// Correct input.
assertThat(underTest.authenticate("password".toList()))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun patternAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern
)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
-
- underTest.clearMessage()
- assertThat(message).isEmpty()
-
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -241,7 +224,6 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength)
assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -255,7 +237,6 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(underTest.authenticate(tooShortPattern))
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -263,51 +244,6 @@ class BouncerInteractorTest : SysuiTestCase() {
// Correct input.
assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(true)
- runCurrent()
-
- underTest.showOrUnlockDevice()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
- utils.deviceEntryRepository.setUnlocked(false)
-
- underTest.showOrUnlockDevice()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_customMessageShown() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val message by collectLastValue(underTest.message)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
-
- val customMessage = "Hello there!"
- underTest.showOrUnlockDevice(customMessage)
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(customMessage)
}
@Test
@@ -316,15 +252,9 @@ class BouncerInteractorTest : SysuiTestCase() {
val isThrottled by collectLastValue(underTest.isThrottled)
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- runCurrent()
- underTest.showOrUnlockDevice()
- runCurrent()
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times ->
// Wrong PIN.
assertThat(underTest.authenticate(listOf(6, 7, 8, 9)))
@@ -353,7 +283,6 @@ class BouncerInteractorTest : SysuiTestCase() {
// Correct PIN, but throttled, so doesn't change away from the bouncer scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertTryAgainMessage(
message,
FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
@@ -379,42 +308,39 @@ class BouncerInteractorTest : SysuiTestCase() {
FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
)
)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
// Correct PIN and no longer throttled so changes to the Gone scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Gone)
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
@Test
- fun hide_whenOnBouncerScene_hidesBouncerAndGoesToLockscreenScene() =
+ fun imeHiddenEvent_isTriggered() =
testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val bouncerSceneKey = currentScene?.key
- assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
+ val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+ runCurrent()
underTest.onImeHidden()
+ runCurrent()
- assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
+ assertThat(imeHiddenEvent).isNotNull()
}
@Test
- fun hide_whenNotOnBouncerScene_doesNothing() =
+ fun intentionalUserInputEvent_registersTouchEvent() =
testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "")
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val notBouncerSceneKey = currentScene?.key
- assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
-
- underTest.onImeHidden()
+ assertThat(utils.powerRepository.userTouchRegistered).isFalse()
+ underTest.onIntentionalUserInput()
+ assertThat(utils.powerRepository.userTouchRegistered).isTrue()
+ }
- assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
+ @Test
+ fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
+ testScope.runTest {
+ underTest.onIntentionalUserInput()
+ verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput()
}
private fun assertTryAgainMessage(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
new file mode 100644
index 000000000000..8c53c0e3f267
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
@@ -0,0 +1,351 @@
+/*
+ * 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.bouncer.domain.interactor
+
+import android.content.res.Resources
+import android.telephony.PinResult
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeSimBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor.Companion.INVALID_SUBSCRIPTION_ID
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
+import com.android.systemui.scene.SceneTestUtils
+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.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class SimBouncerInteractorTest : SysuiTestCase() {
+ @Mock lateinit var telephonyManager: TelephonyManager
+ @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock lateinit var euiccManager: EuiccManager
+
+ private val utils = SceneTestUtils(this)
+ private val bouncerSimRepository = FakeSimBouncerRepository()
+ private val resources: Resources = context.resources
+ private val testScope = utils.testScope
+
+ private lateinit var underTest: SimBouncerInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ SimBouncerInteractor(
+ context,
+ testScope.backgroundScope,
+ utils.testDispatcher,
+ bouncerSimRepository,
+ telephonyManager,
+ resources,
+ keyguardUpdateMonitor,
+ euiccManager,
+ utils.mobileConnectionsRepository,
+ )
+ }
+
+ @Test
+ fun getDefaultMessage() {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setDisplayName("sim").build()
+ )
+ whenever(telephonyManager.activeModemCount).thenReturn(1)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_sim_pin_instructions))
+ }
+
+ @Test
+ fun getDefaultMessage_isPuk() {
+ bouncerSimRepository.setSimPukLocked(true)
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setDisplayName("sim").build()
+ )
+ whenever(telephonyManager.activeModemCount).thenReturn(1)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_puk_enter_puk_hint))
+ }
+
+ @Test
+ fun getDefaultMessage_isEsimLocked() {
+ bouncerSimRepository.setLockedEsim(true)
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setDisplayName("sim").build()
+ )
+ whenever(telephonyManager.activeModemCount).thenReturn(1)
+
+ val msg = resources.getString(R.string.kg_sim_pin_instructions)
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_sim_lock_esim_instructions, msg))
+ }
+
+ @Test
+ fun getDefaultMessage_multipleSims() {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setDisplayName("sim").build()
+ )
+ whenever(telephonyManager.activeModemCount).thenReturn(2)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_sim_pin_instructions_multi, "sim"))
+ }
+
+ @Test
+ fun getDefaultMessage_multipleSims_isPuk() {
+ bouncerSimRepository.setSimPukLocked(true)
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setDisplayName("sim").build()
+ )
+ whenever(telephonyManager.activeModemCount).thenReturn(2)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_puk_enter_puk_hint_multi, "sim"))
+ }
+
+ @Test
+ fun getDefaultMessage_multipleSims_emptyDisplayName() {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(SubscriptionInfo.Builder().build())
+ whenever(telephonyManager.activeModemCount).thenReturn(2)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_sim_pin_instructions))
+ }
+
+ @Test
+ fun getDefaultMessage_multipleSims_emptyDisplayName_isPuk() {
+ bouncerSimRepository.setSimPukLocked(true)
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setActiveSubscriptionInfo(SubscriptionInfo.Builder().build())
+ whenever(telephonyManager.activeModemCount).thenReturn(2)
+
+ assertThat(underTest.getDefaultMessage())
+ .isEqualTo(resources.getString(R.string.kg_puk_enter_puk_hint))
+ }
+
+ @Test
+ fun resetSimPukUserInput() {
+ bouncerSimRepository.setSimPukUserInput("00000000", "1234")
+
+ assertThat(bouncerSimRepository.simPukInputModel.enteredSimPuk).isEqualTo("00000000")
+ assertThat(bouncerSimRepository.simPukInputModel.enteredSimPin).isEqualTo("1234")
+
+ underTest.resetSimPukUserInput()
+
+ assertThat(bouncerSimRepository.simPukInputModel.enteredSimPuk).isNull()
+ assertThat(bouncerSimRepository.simPukInputModel.enteredSimPin).isNull()
+ }
+
+ @Test
+ fun disableEsim() =
+ testScope.runTest {
+ val portIndex = 1
+ bouncerSimRepository.setActiveSubscriptionInfo(
+ SubscriptionInfo.Builder().setPortIndex(portIndex).build()
+ )
+
+ underTest.disableEsim()
+ runCurrent()
+
+ verify(euiccManager)
+ .switchToSubscription(
+ eq(INVALID_SUBSCRIPTION_ID),
+ eq(portIndex),
+ ArgumentMatchers.any()
+ )
+ }
+
+ @Test
+ fun verifySimPin() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(false)
+ whenever(telephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(telephonyManager)
+ whenever(telephonyManager.supplyIccLockPin(anyString()))
+ .thenReturn(PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 1))
+
+ val msg: String? = underTest.verifySim(listOf(0, 0, 0, 0))
+ runCurrent()
+ assertThat(msg).isNull()
+
+ verify(keyguardUpdateMonitor).reportSimUnlocked(1)
+ }
+
+ @Test
+ fun verifySimPin_incorrect_oneRemainingAttempt() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(false)
+ whenever(telephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(telephonyManager)
+ whenever(telephonyManager.supplyIccLockPin(anyString()))
+ .thenReturn(
+ PinResult(
+ PinResult.PIN_RESULT_TYPE_INCORRECT,
+ 1,
+ )
+ )
+
+ val msg: String? = underTest.verifySim(listOf(0, 0, 0, 0))
+ runCurrent()
+
+ assertThat(msg).isNull()
+ val errorDialogMessage by collectLastValue(bouncerSimRepository.errorDialogMessage)
+ assertThat(errorDialogMessage)
+ .isEqualTo(
+ "Enter SIM PIN. You have 1 remaining attempt before you must contact" +
+ " your carrier to unlock your device."
+ )
+ }
+
+ @Test
+ fun verifySimPin_incorrect_threeRemainingAttempts() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(false)
+ whenever(telephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(telephonyManager)
+ whenever(telephonyManager.supplyIccLockPin(anyString()))
+ .thenReturn(
+ PinResult(
+ PinResult.PIN_RESULT_TYPE_INCORRECT,
+ 3,
+ )
+ )
+
+ val msg = underTest.verifySim(listOf(0, 0, 0, 0))
+ runCurrent()
+
+ assertThat(msg).isEqualTo("Enter SIM PIN. You have 3 remaining attempts.")
+ }
+
+ @Test
+ fun verifySimPin_notCorrectLength_tooShort() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(false)
+
+ val msg = underTest.verifySim(listOf(0))
+
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_invalid_sim_pin_hint))
+ }
+
+ @Test
+ fun verifySimPin_notCorrectLength_tooLong() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(false)
+
+ val msg = underTest.verifySim(listOf(0, 0, 0, 0, 0, 0, 0, 0, 0))
+
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_invalid_sim_pin_hint))
+ }
+
+ @Test
+ fun verifySimPuk() =
+ testScope.runTest {
+ whenever(telephonyManager.createForSubscriptionId(anyInt()))
+ .thenReturn(telephonyManager)
+ whenever(telephonyManager.supplyIccLockPuk(anyString(), anyString()))
+ .thenReturn(PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 1))
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(true)
+
+ var msg = underTest.verifySim(listOf(0, 0, 0, 0, 0, 0, 0, 0, 0))
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_puk_enter_pin_hint))
+
+ msg = underTest.verifySim(listOf(0, 0, 0, 0))
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_enter_confirm_pin_hint))
+
+ msg = underTest.verifySim(listOf(0, 0, 0, 0))
+ assertThat(msg).isNull()
+
+ runCurrent()
+ verify(keyguardUpdateMonitor).reportSimUnlocked(1)
+ }
+
+ @Test
+ fun verifySimPuk_inputTooShort() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(true)
+ val msg = underTest.verifySim(listOf(0, 0, 0, 0))
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_invalid_sim_puk_hint))
+ }
+
+ @Test
+ fun verifySimPuk_pinNotCorrectLength() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(true)
+
+ underTest.verifySim(listOf(0, 0, 0, 0, 0, 0, 0, 0, 0))
+
+ val msg = underTest.verifySim(listOf(0, 0, 0))
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_invalid_sim_pin_hint))
+ }
+
+ @Test
+ fun verifySimPuk_confirmedPinDoesNotMatch() =
+ testScope.runTest {
+ bouncerSimRepository.setSubscriptionId(1)
+ bouncerSimRepository.setSimPukLocked(true)
+
+ underTest.verifySim(listOf(0, 0, 0, 0, 0, 0, 0, 0, 0))
+ underTest.verifySim(listOf(0, 0, 0, 0))
+
+ val msg = underTest.verifySim(listOf(0, 0, 0, 1))
+ assertThat(msg).isEqualTo(resources.getString(R.string.kg_puk_enter_pin_hint))
+ }
+
+ @Test
+ fun onErrorDialogDismissed_clearsErrorDialogMessageInRepository() {
+ bouncerSimRepository.setSimVerificationErrorMessage("abc")
+ assertThat(bouncerSimRepository.errorDialogMessage.value).isNotNull()
+
+ underTest.onErrorDialogDismissed()
+
+ assertThat(bouncerSimRepository.errorDialogMessage.value).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
new file mode 100644
index 000000000000..395d7129bee3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
@@ -0,0 +1,253 @@
+/*
+ * 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.bouncer.ui.helper
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SIDE_BY_SIDE
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SPLIT
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STACKED
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BouncerSceneLayoutTest : SysuiTestCase() {
+
+ data object Phone :
+ Device(
+ name = "phone",
+ width = SizeClass.COMPACT,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+ data object Tablet :
+ Device(
+ name = "tablet",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Horizontally,
+ )
+ data object Folded :
+ Device(
+ name = "folded",
+ width = SizeClass.COMPACT,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Vertically,
+ )
+ data object Unfolded :
+ Device(
+ name = "unfolded",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Vertically,
+ widthWhenUnnaturallyHeld = SizeClass.MEDIUM,
+ heightWhenUnnaturallyHeld = SizeClass.MEDIUM,
+ )
+ data object TallerFolded :
+ Device(
+ name = "taller folded",
+ width = SizeClass.COMPACT,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+ data object TallerUnfolded :
+ Device(
+ name = "taller unfolded",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun testCases() =
+ listOf(
+ Phone to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ Tablet to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = STACKED,
+ ),
+ Folded to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ Unfolded to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = STANDARD,
+ ),
+ TallerFolded to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ TallerUnfolded to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = SIDE_BY_SIDE,
+ ),
+ )
+ .flatMap { (device, expected) ->
+ buildList {
+ // Holding the device in its natural orientation (vertical or horizontal):
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld,
+ expected = expected.layout(heldNaturally = true),
+ )
+ )
+
+ if (expected.whenNaturallyHeld == SIDE_BY_SIDE) {
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld,
+ isSideBySideSupported = false,
+ expected = STANDARD,
+ )
+ )
+ }
+
+ // Holding the device the other way:
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld.flip(),
+ expected = expected.layout(heldNaturally = false),
+ )
+ )
+
+ if (expected.whenUnnaturallyHeld == SIDE_BY_SIDE) {
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld.flip(),
+ isSideBySideSupported = false,
+ expected = STANDARD,
+ )
+ )
+ }
+ }
+ }
+ }
+
+ @Parameterized.Parameter @JvmField var testCase: TestCase? = null
+
+ @Test
+ fun calculateLayout() {
+ testCase?.let { nonNullTestCase ->
+ with(nonNullTestCase) {
+ assertThat(
+ calculateLayoutInternal(
+ width = device.width(whenHeld = held),
+ height = device.height(whenHeld = held),
+ isSideBySideSupported = isSideBySideSupported,
+ )
+ )
+ .isEqualTo(expected)
+ }
+ }
+ }
+
+ data class TestCase(
+ val device: Device,
+ val held: Held,
+ val expected: BouncerSceneLayout,
+ val isSideBySideSupported: Boolean = true,
+ ) {
+ override fun toString(): String {
+ return buildString {
+ append(device.name)
+ append(" width: ${device.width(held).name.lowercase(Locale.US)}")
+ append(" height: ${device.height(held).name.lowercase(Locale.US)}")
+ append(" when held $held")
+ if (!isSideBySideSupported) {
+ append(" (side-by-side not supported)")
+ }
+ }
+ }
+ }
+
+ data class Expected(
+ val whenNaturallyHeld: BouncerSceneLayout,
+ val whenUnnaturallyHeld: BouncerSceneLayout,
+ ) {
+ fun layout(heldNaturally: Boolean): BouncerSceneLayout {
+ return if (heldNaturally) {
+ whenNaturallyHeld
+ } else {
+ whenUnnaturallyHeld
+ }
+ }
+ }
+
+ sealed class Device(
+ val name: String,
+ private val width: SizeClass,
+ private val height: SizeClass,
+ val naturallyHeld: Held,
+ private val widthWhenUnnaturallyHeld: SizeClass = height,
+ private val heightWhenUnnaturallyHeld: SizeClass = width,
+ ) {
+ fun width(whenHeld: Held): SizeClass {
+ return if (isHeldNaturally(whenHeld)) {
+ width
+ } else {
+ widthWhenUnnaturallyHeld
+ }
+ }
+
+ fun height(whenHeld: Held): SizeClass {
+ return if (isHeldNaturally(whenHeld)) {
+ height
+ } else {
+ heightWhenUnnaturallyHeld
+ }
+ }
+
+ private fun isHeldNaturally(whenHeld: Held): Boolean {
+ return whenHeld == naturallyHeld
+ }
+ }
+
+ sealed class Held {
+ abstract fun flip(): Held
+ }
+ data object Vertically : Held() {
+ override fun flip(): Held {
+ return Horizontally
+ }
+ }
+ data object Horizontally : Held() {
+ override fun flip(): Held {
+ return Vertically
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 8e1f5ac58b68..63c992bd7854 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -37,24 +37,19 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
+ private val bouncerInteractor =
+ utils.bouncerInteractor(
+ authenticationInteractor = utils.authenticationInteractor(),
)
private val underTest =
PinBouncerViewModel(
applicationContext = context,
viewModelScope = testScope.backgroundScope,
- interactor =
- utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
+ interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true),
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationMethod = AuthenticationMethodModel.Pin,
)
@Test
@@ -85,18 +80,14 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
@Test
fun onImeVisibilityChanged() =
testScope.runTest {
- val desiredScene by collectLastValue(sceneInteractor.desiredScene)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
-
- underTest.onImeVisibilityChanged(false)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+ val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
underTest.onImeVisibilityChanged(true)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+ assertThat(onImeHidden).isNull()
underTest.onImeVisibilityChanged(false)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Lockscreen)
+ assertThat(onImeHidden).isNotNull()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 6ef518e3ab51..75d6a007b4aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -19,10 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
@@ -48,16 +46,9 @@ class BouncerViewModelTest : SysuiTestCase() {
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
)
private val underTest =
utils.bouncerViewModel(
@@ -96,8 +87,7 @@ class BouncerViewModelTest : SysuiTestCase() {
@Test
fun authMethodChanged_doesNotReuseInstances() =
testScope.runTest {
- val seen =
- mutableMapOf<DomainLayerAuthenticationMethodModel, AuthMethodBouncerViewModel>()
+ val seen = mutableMapOf<AuthenticationMethodModel, AuthMethodBouncerViewModel>()
val authMethodViewModel: AuthMethodBouncerViewModel? by
collectLastValue(underTest.authMethodViewModel)
@@ -137,7 +127,7 @@ class BouncerViewModelTest : SysuiTestCase() {
@Test
fun authMethodsToTest_returnsCompleteSampleOfAllAuthMethodTypes() {
assertThat(authMethodsToTest().map { it::class }.toSet())
- .isEqualTo(DomainLayerAuthenticationMethodModel::class.sealedSubclasses.toSet())
+ .isEqualTo(AuthenticationMethodModel::class.sealedSubclasses.toSet())
}
@Test
@@ -145,9 +135,7 @@ class BouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(message?.isUpdateAnimated).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
@@ -170,9 +158,7 @@ class BouncerViewModelTest : SysuiTestCase() {
}
)
val throttling by collectLastValue(bouncerInteractor.throttling)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(isInputEnabled).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
@@ -189,9 +175,7 @@ class BouncerViewModelTest : SysuiTestCase() {
fun throttlingDialogMessage() =
testScope.runTest {
val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
// Wrong PIN.
@@ -226,34 +210,30 @@ class BouncerViewModelTest : SysuiTestCase() {
assertThat(isSideBySideSupported).isFalse()
}
- private fun authMethodsToTest(): List<DomainLayerAuthenticationMethodModel> {
- return listOf(
- DomainLayerAuthenticationMethodModel.None,
- DomainLayerAuthenticationMethodModel.Swipe,
- DomainLayerAuthenticationMethodModel.Pin,
- DomainLayerAuthenticationMethodModel.Password,
- DomainLayerAuthenticationMethodModel.Pattern,
- )
- }
+ @Test
+ fun isFoldSplitRequired() =
+ testScope.runTest {
+ val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ assertThat(isFoldSplitRequired).isTrue()
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ assertThat(isFoldSplitRequired).isFalse()
- private fun FakeAuthenticationRepository.setAuthenticationMethod(
- model: DomainLayerAuthenticationMethodModel,
- ) {
- setAuthenticationMethod(
- when (model) {
- is DomainLayerAuthenticationMethodModel.None,
- is DomainLayerAuthenticationMethodModel.Swipe ->
- DataLayerAuthenticationMethodModel.None
- is DomainLayerAuthenticationMethodModel.Pin ->
- DataLayerAuthenticationMethodModel.Pin
- is DomainLayerAuthenticationMethodModel.Password ->
- DataLayerAuthenticationMethodModel.Password
- is DomainLayerAuthenticationMethodModel.Pattern ->
- DataLayerAuthenticationMethodModel.Pattern
- }
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- model !is DomainLayerAuthenticationMethodModel.None
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pattern
+ )
+ assertThat(isFoldSplitRequired).isTrue()
+ }
+
+ private fun authMethodsToTest(): List<AuthenticationMethodModel> {
+ return listOf(
+ AuthenticationMethodModel.None,
+ AuthenticationMethodModel.Pin,
+ AuthenticationMethodModel.Password,
+ AuthenticationMethodModel.Pattern,
+ AuthenticationMethodModel.Sim,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 390742031381..9b1e9585979a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,8 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
@@ -46,16 +45,9 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -86,10 +78,25 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
lockDeviceAndOpenPasswordBouncer()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Password)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password)
+ }
+
+ @Test
+ fun onHidden_resetsPasswordInputAndMessage() =
+ testScope.runTest {
+ val message by collectLastValue(bouncerViewModel.message)
+ val password by collectLastValue(underTest.password)
+ lockDeviceAndOpenPasswordBouncer()
+
+ underTest.onPasswordInputChanged("password")
+ assertThat(message?.text).isNotEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(password).isNotEmpty()
+
+ underTest.onHidden()
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(password).isEmpty()
}
@Test
@@ -110,19 +117,19 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
@Test
fun onAuthenticateKeyPressed_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPasswordBouncer()
underTest.onPasswordInputChanged("password")
underTest.onAuthenticateKeyPressed()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
fun onAuthenticateKeyPressed_whenWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
lockDeviceAndOpenPasswordBouncer()
@@ -130,38 +137,34 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_whenEmpty() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- // Enter nothing.
+ switchToScene(SceneKey.Bouncer)
+
+ // No input entered.
underTest.onAuthenticateKeyPressed()
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
lockDeviceAndOpenPasswordBouncer()
@@ -171,7 +174,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct password:
underTest.onPasswordInputChanged("password")
@@ -179,7 +182,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateKeyPressed()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -194,32 +197,33 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(password).isEqualTo("password")
// The user doesn't confirm the password, but navigates back to the lockscreen instead.
- sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ switchToScene(SceneKey.Lockscreen)
// The user navigates to the bouncer again.
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ switchToScene(SceneKey.Bouncer)
// Ensure the previously-entered password is not shown.
assertThat(password).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPasswordBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 47db4f8faeca..862c39c9d4cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -19,9 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
@@ -49,16 +48,9 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -96,8 +88,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Pattern)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pattern)
}
@Test
@@ -120,7 +111,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
@Test
fun onDragEnd_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
lockDeviceAndOpenPatternBouncer()
@@ -150,7 +142,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragEnd()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -330,7 +322,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
xPx = 30f * coordinate.x + 15,
yPx = 30f * coordinate.y + 15,
containerSizePx = 90,
- verticalOffsetPx = 0f,
)
}
@@ -344,7 +335,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
@Test
fun onDragEnd_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
@@ -356,14 +348,14 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(message?.text).isEqualTo(WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct pattern:
CORRECT_PATTERN.forEach(::dragToCoordinate)
underTest.onDragEnd()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
private fun dragOverCoordinates(vararg coordinatesDragged: Point) {
@@ -376,19 +368,26 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
xPx = dotSize * coordinate.x + 15f,
yPx = dotSize * coordinate.y + 15f,
containerSizePx = containerSize,
- verticalOffsetPx = 0f,
)
}
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPatternBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 3ddac7e1ad1d..c30e405ab911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -19,9 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
@@ -48,16 +47,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor = utils.authenticationInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -71,6 +63,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
viewModelScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationMethod = AuthenticationMethodModel.Pin,
)
@Before
@@ -82,50 +76,77 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ lockDeviceAndOpenPinBouncer()
assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN)
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Pin)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ }
+
+ @Test
+ fun simBouncerViewModel_simAreaIsVisible() =
+ testScope.runTest {
+ val underTest =
+ PinBouncerViewModel(
+ applicationContext = context,
+ viewModelScope = testScope.backgroundScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = MutableStateFlow(true).asStateFlow(),
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationMethod = AuthenticationMethodModel.Sim,
+ )
+
+ assertThat(underTest.isSimAreaVisible).isTrue()
+ }
+
+ @Test
+ fun onErrorDialogDismissed_clearsDialogMessage() =
+ testScope.runTest {
+ val dialogMessage by collectLastValue(underTest.errorDialogMessage)
+ utils.simBouncerRepository.setSimVerificationErrorMessage("abc")
+ assertThat(dialogMessage).isEqualTo("abc")
+
+ underTest.onErrorDialogDismissed()
+
+ assertThat(dialogMessage).isNull()
+ }
+
+ @Test
+ fun simBouncerViewModel_autoConfirmEnabled_hintedPinLengthIsNull() =
+ testScope.runTest {
+ val underTest =
+ PinBouncerViewModel(
+ applicationContext = context,
+ viewModelScope = testScope.backgroundScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = MutableStateFlow(true).asStateFlow(),
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationMethod = AuthenticationMethodModel.Sim,
+ )
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+ val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+
+ assertThat(hintedPinLength).isNull()
}
@Test
fun onPinButtonClicked() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ lockDeviceAndOpenPinBouncer()
underTest.onPinButtonClicked(1)
assertThat(message?.text).isEmpty()
assertThat(pin).containsExactly(1)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onBackspaceButtonClicked() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
@@ -137,7 +158,6 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
@@ -181,16 +201,15 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onAuthenticateButtonClicked_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
underTest.onAuthenticateButtonClicked()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -217,7 +236,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onAuthenticateButtonClicked_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
@@ -230,31 +250,28 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct PIN:
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
assertThat(message?.text).isEmpty()
underTest.onAuthenticateButtonClicked()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
fun onAutoConfirm_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -263,7 +280,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
@@ -281,31 +298,21 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown_againAfterSceneChange_resetsPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
// The user types a PIN.
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
assertThat(pin).isNotEmpty()
// The user doesn't confirm the PIN, but navigates back to the lockscreen instead.
- sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ switchToScene(SceneKey.Lockscreen)
// The user navigates to the bouncer again.
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ switchToScene(SceneKey.Bouncer)
// Ensure the previously-entered PIN is not shown.
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
@@ -323,7 +330,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
@@ -333,7 +340,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
underTest.onPinButtonClicked(1)
@@ -355,21 +362,40 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
+ @Test
+ fun isDigitButtonAnimationEnabled() =
+ testScope.runTest {
+ val isAnimationEnabled by collectLastValue(underTest.isDigitButtonAnimationEnabled)
+
+ utils.authenticationRepository.setPinEnhancedPrivacyEnabled(true)
+ assertThat(isAnimationEnabled).isFalse()
+
+ utils.authenticationRepository.setPinEnhancedPrivacyEnabled(false)
+ assertThat(isAnimationEnabled).isTrue()
+ }
+
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPinBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt
new file mode 100644
index 000000000000..bfa36412ceb5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/domain/interactor/ConfigurationInteractorTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.common.domain.interactor
+
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_90
+import android.view.Surface.Rotation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+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(AndroidTestingRunner::class)
+open class ConfigurationInteractorTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val configurationController = FakeConfigurationController()
+ private val configurationRepository =
+ ConfigurationRepositoryImpl(
+ configurationController,
+ context,
+ testScope.backgroundScope,
+ mock()
+ )
+
+ private lateinit var configuration: Configuration
+ private lateinit var underTest: ConfigurationInteractor
+
+ @Before
+ fun setUp() {
+ configuration = context.resources.configuration
+
+ val testableResources = context.getOrCreateTestableResources()
+ testableResources.overrideConfiguration(configuration)
+
+ underTest = ConfigurationInteractorImpl(configurationRepository)
+ }
+
+ @Test
+ fun maxBoundsChange_emitsMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(width = DISPLAY_WIDTH * 2, height = DISPLAY_HEIGHT * 3)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ Rect(0, 0, DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 3),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun maxBoundsSameOnConfigChange_doesNotEmitMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ @Test
+ fun firstMaxBoundsChange_emitsMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ @Test
+ fun displayRotatedButMaxBoundsTheSame_doesNotEmitNewMaxBoundsChange() =
+ testScope.runTest {
+ val values by collectValues(underTest.naturalMaxBounds)
+
+ updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
+ runCurrent()
+ updateDisplay(width = DISPLAY_HEIGHT, height = DISPLAY_WIDTH, rotation = ROTATION_90)
+ runCurrent()
+
+ assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT))
+ }
+
+ private fun updateDisplay(
+ width: Int = DISPLAY_WIDTH,
+ height: Int = DISPLAY_HEIGHT,
+ @Rotation rotation: Int = ROTATION_0
+ ) {
+ configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
+ configuration.windowConfiguration.displayRotation = rotation
+
+ configurationController.onConfigurationChanged(configuration)
+ }
+
+ private companion object {
+ private const val DISPLAY_WIDTH = 100
+ private const val DISPLAY_HEIGHT = 200
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
index 14ec4d44ab8c..16b2ed633f11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -124,6 +124,39 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
assertThat(widgets()).containsExactly(communalItemRankEntry2, communalWidgetItemEntry2)
}
+ @Test
+ fun reorderWidget_emitsWidgetsInNewOrder() =
+ testScope.runTest {
+ val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+ val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+ widgetsToAdd.forEach {
+ val (widgetId, provider, priority) = it
+ communalWidgetDao.addWidget(
+ widgetId = widgetId,
+ provider = provider,
+ priority = priority,
+ )
+ }
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry1,
+ communalWidgetItemEntry1,
+ communalItemRankEntry2,
+ communalWidgetItemEntry2
+ )
+
+ val widgetIdsInNewOrder = listOf(widgetInfo2.widgetId, widgetInfo1.widgetId)
+ communalWidgetDao.updateWidgetOrder(widgetIdsInNewOrder)
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry2,
+ communalWidgetItemEntry2,
+ communalItemRankEntry1,
+ communalWidgetItemEntry1
+ )
+ }
+
data class FakeWidgetMetadata(
val widgetId: Int,
val provider: ComponentName,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ca8316dce10e..182712a13174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -21,7 +21,6 @@ import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
import android.content.ComponentName
-import android.content.pm.PackageManager
import android.os.UserHandle
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,8 +33,6 @@ 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.coroutines.collectLastValue
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.res.R
@@ -72,16 +69,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var packageManager: PackageManager
-
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var userHandle: UserHandle
@Mock private lateinit var userTracker: UserTracker
- @Mock private lateinit var featureFlags: FeatureFlagsClassic
-
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
@Mock private lateinit var providerInfoA: AppWidgetProviderInfo
@@ -113,13 +106,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
communalRepository = FakeCommunalRepository()
communalEnabled(true)
- widgetOnKeyguardEnabled(true)
setAppWidgetIds(emptyList())
overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
whenever(userTracker.userHandle).thenReturn(userHandle)
+ whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
}
@Test
@@ -209,11 +202,25 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun reorderWidgets_queryDb() =
+ testScope.runTest {
+ userUnlocked(true)
+ val repository = initCommunalWidgetRepository()
+ runCurrent()
+
+ val ids = listOf(104, 103, 101)
+ repository.updateWidgetOrder(ids)
+ runCurrent()
+
+ verify(communalWidgetDao).updateWidgetOrder(ids)
+ }
+
+ @Test
fun broadcastReceiver_communalDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
testScope.runTest {
communalEnabled(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverNeverRegistered()
}
@@ -222,7 +229,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
userUnlocked(true)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverNeverRegistered()
}
@@ -231,7 +238,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverRegistered()
}
@@ -241,7 +248,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- val job = launch { repository.stopwatchAppWidgetInfo.collect() }
+ val job = launch { repository.communalWidgets.collect() }
runCurrent()
val receiver = broadcastReceiverUpdate()
@@ -252,53 +259,16 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun stopwatch_whenUserUnlocks_receiveProviderInfo() =
- testScope.runTest {
- userUnlocked(false)
- val repository = initCommunalWidgetRepository()
- val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
- assertThat(lastStopwatchProviderInfo()).isNull()
-
- userUnlocked(true)
- installedProviders(listOf(stopwatchProviderInfo))
- broadcastReceiverUpdate()
-
- assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo)
- }
-
- @Test
- fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() =
- testScope.runTest {
- userUnlocked(true)
- installedProviders(listOf())
-
- val repository = initCommunalWidgetRepository()
-
- val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
- assertThat(lastStopwatchProviderInfo()).isNull()
- }
-
- @Test
- fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() =
- testScope.runTest {
- userUnlocked(true)
- installedProviders(listOf(stopwatchProviderInfo))
- val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
- verify(appWidgetHost).allocateAppWidgetId()
- }
-
- @Test
fun appWidgetHost_userUnlocked_startListening() =
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost, Mockito.never()).startListening()
userUnlocked(true)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).startListening()
}
@@ -308,18 +278,18 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
userUnlocked(true)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).startListening()
verify(appWidgetHost, Mockito.never()).stopListening()
userUnlocked(false)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).stopListening()
}
@@ -334,11 +304,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
communalRepository,
communalWidgetHost,
communalWidgetDao,
- packageManager,
userManager,
userTracker,
logBuffer,
- featureFlags,
)
}
@@ -385,10 +353,6 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
communalRepository.setIsCommunalEnabled(enabled)
}
- private fun widgetOnKeyguardEnabled(enabled: Boolean) {
- whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
- }
-
private fun userUnlocked(userUnlocked: Boolean) {
whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 08d54c001d11..16cfa2398fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -18,7 +18,6 @@
package com.android.systemui.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
-import android.provider.Settings
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,9 +28,9 @@ import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -45,16 +44,14 @@ 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.mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class CommunalInteractorTest : SysuiTestCase() {
- @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo
-
private lateinit var testScope: TestScope
private lateinit var tutorialRepository: FakeCommunalTutorialRepository
@@ -63,6 +60,7 @@ class CommunalInteractorTest : SysuiTestCase() {
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var smartspaceRepository: FakeSmartspaceRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
private lateinit var underTest: CommunalInteractor
@@ -80,23 +78,12 @@ class CommunalInteractorTest : SysuiTestCase() {
widgetRepository = withDeps.widgetRepository
smartspaceRepository = withDeps.smartspaceRepository
keyguardRepository = withDeps.keyguardRepository
+ editWidgetsActivityStarter = withDeps.editWidgetsActivityStarter
underTest = withDeps.communalInteractor
}
@Test
- fun appWidgetInfoFlow() =
- testScope.runTest {
- val lastAppWidgetInfo = collectLastValue(underTest.appWidgetInfo)
- runCurrent()
- assertThat(lastAppWidgetInfo()).isNull()
-
- widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo)
- runCurrent()
- assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
- }
-
- @Test
fun communalEnabled() =
testScope.runTest {
communalRepository.setIsCommunalEnabled(true)
@@ -111,24 +98,6 @@ class CommunalInteractorTest : SysuiTestCase() {
}
@Test
- fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
- testScope.runTest {
- // Keyguard showing, and tutorial not started.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(
- Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
- )
-
- val communalContent by collectLastValue(underTest.communalContent)
-
- assertThat(communalContent!!).isNotEmpty()
- communalContent!!.forEach { model ->
- assertThat(model is CommunalContentModel.Tutorial).isTrue()
- }
- }
-
- @Test
fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
testScope.runTest {
// Keyguard showing, and tutorial completed.
@@ -157,12 +126,11 @@ class CommunalInteractorTest : SysuiTestCase() {
)
widgetRepository.setCommunalWidgets(widgets)
- val communalContent by collectLastValue(underTest.communalContent)
+ val widgetContent by collectLastValue(underTest.widgetContent)
- assertThat(communalContent!!).isNotEmpty()
- communalContent!!.forEachIndexed { index, model ->
- assertThat((model as CommunalContentModel.Widget).appWidgetId)
- .isEqualTo(widgets[index].appWidgetId)
+ assertThat(widgetContent!!).isNotEmpty()
+ widgetContent!!.forEachIndexed { index, model ->
+ assertThat(model.appWidgetId).isEqualTo(widgets[index].appWidgetId)
}
}
@@ -195,48 +163,9 @@ class CommunalInteractorTest : SysuiTestCase() {
val targets = listOf(target1, target2, target3)
smartspaceRepository.setLockscreenSmartspaceTargets(targets)
- val communalContent by collectLastValue(underTest.communalContent)
- assertThat(communalContent?.size).isEqualTo(1)
- assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target3")
- }
-
- @Test
- fun smartspace_smartspaceAndWidgetsAvailable_showSmartspaceAndWidgetContent() =
- testScope.runTest {
- // Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- // Widgets available.
- val widgets =
- listOf(
- CommunalWidgetContentModel(
- appWidgetId = 0,
- priority = 30,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 1,
- priority = 20,
- providerInfo = mock(),
- ),
- )
- widgetRepository.setCommunalWidgets(widgets)
-
- // Smartspace available.
- val target = mock(SmartspaceTarget::class.java)
- whenever(target.smartspaceTargetId).thenReturn("target")
- whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
- whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
- smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
-
- val communalContent by collectLastValue(underTest.communalContent)
-
- assertThat(communalContent?.size).isEqualTo(3)
- assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target")
- assertThat(communalContent?.get(1)?.key).isEqualTo("widget_0")
- assertThat(communalContent?.get(2)?.key).isEqualTo("widget_1")
+ val smartspaceContent by collectLastValue(underTest.smartspaceContent)
+ assertThat(smartspaceContent?.size).isEqualTo(1)
+ assertThat(smartspaceContent?.get(0)?.key).isEqualTo("smartspace_target3")
}
@Test
@@ -248,55 +177,11 @@ class CommunalInteractorTest : SysuiTestCase() {
// Media is playing.
mediaRepository.mediaPlaying.value = true
- val communalContent by collectLastValue(underTest.communalContent)
+ val umoContent by collectLastValue(underTest.umoContent)
- assertThat(communalContent?.size).isEqualTo(1)
- assertThat(communalContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
- assertThat(communalContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
- }
-
- @Test
- fun contentOrdering() =
- testScope.runTest {
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- // Widgets available.
- val widgets =
- listOf(
- CommunalWidgetContentModel(
- appWidgetId = 0,
- priority = 30,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 1,
- priority = 20,
- providerInfo = mock(),
- ),
- )
- widgetRepository.setCommunalWidgets(widgets)
-
- // Smartspace available.
- val target = mock(SmartspaceTarget::class.java)
- whenever(target.smartspaceTargetId).thenReturn("target")
- whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
- whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
- smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
-
- // Media playing.
- mediaRepository.mediaPlaying.value = true
-
- val communalContent by collectLastValue(underTest.communalContent)
-
- // Order is smart space, then UMO, then widget content.
- assertThat(communalContent?.size).isEqualTo(4)
- assertThat(communalContent?.get(0))
- .isInstanceOf(CommunalContentModel.Smartspace::class.java)
- assertThat(communalContent?.get(1)).isInstanceOf(CommunalContentModel.Umo::class.java)
- assertThat(communalContent?.get(2))
- .isInstanceOf(CommunalContentModel.Widget::class.java)
- assertThat(communalContent?.get(3))
- .isInstanceOf(CommunalContentModel.Widget::class.java)
+ assertThat(umoContent?.size).isEqualTo(1)
+ assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(umoContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
}
@Test
@@ -338,4 +223,11 @@ class CommunalInteractorTest : SysuiTestCase() {
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(true)
}
+
+ @Test
+ fun testShowWidgetEditorStartsActivity() =
+ testScope.runTest {
+ underTest.showWidgetEditor()
+ verify(editWidgetsActivityStarter).startActivity()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index 33a666700877..a49629252520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -7,7 +7,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -20,14 +19,13 @@ import org.mockito.MockitoAnnotations
@SmallTest
class DefaultCommunalBlueprintTest : SysuiTestCase() {
@Mock private lateinit var hubSection: DefaultCommunalHubSection
- @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
private lateinit var blueprint: DefaultCommunalBlueprint
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- blueprint = DefaultCommunalBlueprint(hubSection, widgetSection)
+ blueprint = DefaultCommunalBlueprint(hubSection)
}
@Test
@@ -35,7 +33,6 @@ class DefaultCommunalBlueprintTest : SysuiTestCase() {
val constraintLayout = ConstraintLayout(context, null)
blueprint.replaceViews(null, constraintLayout)
verify(hubSection).addViews(constraintLayout)
- verify(widgetSection).addViews(constraintLayout)
}
@Test
@@ -43,6 +40,5 @@ class DefaultCommunalBlueprintTest : SysuiTestCase() {
val cs = ConstraintSet()
blueprint.applyConstraints(cs)
verify(hubSection).applyConstraints(cs)
- verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 2c80035873f0..97ac8c62d69d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -83,16 +83,27 @@ class DeviceEntryRepositoryTest : SysuiTestCase() {
}
@Test
- fun isInsecureLockscreenEnabled() =
+ fun isLockscreenEnabled() =
testScope.runTest {
whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[0].id)).thenReturn(false)
whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[1].id)).thenReturn(true)
userRepository.setSelectedUserInfo(USER_INFOS[0])
- assertThat(underTest.isInsecureLockscreenEnabled()).isTrue()
+ assertThat(underTest.isLockscreenEnabled()).isTrue()
userRepository.setSelectedUserInfo(USER_INFOS[1])
- assertThat(underTest.isInsecureLockscreenEnabled()).isFalse()
+ assertThat(underTest.isLockscreenEnabled()).isFalse()
+ }
+
+ @Test
+ fun reportSuccessfulAuthentication_shouldUpdateIsUnlocked() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isFalse()
+
+ underTest.reportSuccessfulAuthentication()
+
+ assertThat(isUnlocked).isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index aebadc5b5730..0004f52bc1c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -19,7 +19,7 @@ package com.android.systemui.deviceentry.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -60,7 +60,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
utils.deviceEntryRepository.apply {
- setInsecureLockscreenEnabled(false)
+ setLockscreenEnabled(false)
// Toggle isUnlocked, twice.
//
@@ -83,19 +83,27 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
@Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isTrue()
}
@Test
+ fun isUnlocked_whenAuthMethodIsSimAndUnlocked_isFalse() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)
+ utils.deviceEntryRepository.setUnlocked(true)
+
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isFalse()
+ }
+
+ @Test
fun isDeviceEntered_onLockscreenWithSwipe_isFalse() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
assertThat(isDeviceEntered).isFalse()
@@ -105,8 +113,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun isDeviceEntered_onShadeBeforeDismissingLockscreenWithSwipe_isFalse() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Shade)
@@ -118,8 +125,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun isDeviceEntered_afterDismissingLockscreenWithSwipe_isTrue() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -131,8 +137,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun isDeviceEntered_onShadeAfterDismissingLockscreenWithSwipe_isTrue() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -148,7 +153,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern
)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Bouncer)
@@ -160,8 +165,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
@Test
fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
@@ -172,7 +176,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun canSwipeToEnter_onLockscreenWithPin_isFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
switchToScene(SceneKey.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
@@ -182,8 +186,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
@Test
fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -192,6 +195,11 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
assertThat(canSwipeToEnter).isFalse()
}
+ private fun setupSwipeDeviceEntryMethod() {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
+ }
+
@Test
fun canSwipeToEnter_whenTrustedByTrustManager_isTrue() =
testScope.runTest {
@@ -278,12 +286,68 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
}
@Test
+ fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
+ fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
+ fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
fun isBypassEnabled_disabledInRepository_false() =
testScope.runTest {
utils.deviceEntryRepository.setBypassEnabled(false)
assertThat(underTest.isBypassEnabled.value).isFalse()
}
+ @Test
+ fun successfulAuthenticationChallengeAttempt_updatedIsUnlockedState() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isFalse()
+
+ utils.authenticationRepository.reportAuthenticationAttempt(true)
+
+ assertThat(isUnlocked).isTrue()
+ }
+
private fun switchToScene(sceneKey: SceneKey) {
sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
new file mode 100644
index 000000000000..e8eda8096b1e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+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
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryUdfpsInteractorTest : SysuiTestCase() {
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var biometricsRepository: FakeBiometricSettingsRepository
+
+ private lateinit var underTest: DeviceEntryUdfpsInteractor
+
+ @Before
+ fun setUp() {
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ biometricsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = fingerprintAuthRepository,
+ biometricSettingsRepository = biometricsRepository,
+ )
+ }
+
+ @Test
+ fun udfpsSupported_rearFp_false() = runTest {
+ val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+ fingerprintPropertyRepository.supportsRearFps()
+ assertThat(isUdfpsSupported).isFalse()
+ }
+
+ @Test
+ fun udfpsSupoprted() = runTest {
+ val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+ fingerprintPropertyRepository.supportsUdfps()
+ assertThat(isUdfpsSupported).isTrue()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ assertThat(isUdfpsEnrolledAndEnabled).isTrue()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled_rearFp_false() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled_notEnrolledOrEnabled_false() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+ }
+
+ @Test
+ fun isListeningForUdfps() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsUdfps()
+ fingerprintAuthRepository.setIsRunning(true)
+ assertThat(isListeningForUdfps).isTrue()
+ }
+
+ @Test
+ fun isListeningForUdfps_rearFp_false() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsRearFps()
+ fingerprintAuthRepository.setIsRunning(true)
+ assertThat(isListeningForUdfps).isFalse()
+ }
+
+ @Test
+ fun isListeningForUdfps_notRunning_false() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsUdfps()
+ fingerprintAuthRepository.setIsRunning(false)
+ assertThat(isListeningForUdfps).isFalse()
+ }
+}
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 b16c3520d978..d246f0e49e1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -910,6 +910,20 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
assertATMSAndKeyguardViewMediatorStatesMatch();
}
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void testStartKeyguardExitAnimation_whenNotInteractive_doesShowAndUpdatesWM() {
+ // If the exit animation was triggered but the device became non-interactive, make sure
+ // relock happens
+ when(mPowerManager.isInteractive()).thenReturn(false);
+
+ startMockKeyguardExitAnimation();
+ cancelMockKeyguardExitAnimation();
+
+ verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
+ assertATMSAndKeyguardViewMediatorStatesMatch();
+ }
+
/**
* Interactions with the ActivityTaskManagerService and others are posted to an executor that
* doesn't use the testable looper. Use this method to ensure those are run as well.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 8d9bc751fbc9..0b148d14a43f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -45,6 +45,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -223,11 +224,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
alternateBouncerInteractor =
AlternateBouncerInteractor(
bouncerRepository = bouncerRepository,
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository(),
biometricSettingsRepository = biometricSettingsRepository,
systemClock = mock(SystemClock::class.java),
keyguardStateController = FakeKeyguardStateController(),
statusBarStateController = mock(StatusBarStateController::class.java),
keyguardUpdateMonitor = keyguardUpdateMonitor,
+ scope = testScope.backgroundScope,
)
displayRepository = FakeDisplayRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 799bd5ac5739..7242cb20dc77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,15 +61,11 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
fakeKeyguardRepository = FakeKeyguardRepository()
powerRepository = FakePowerRepository()
- powerInteractor = PowerInteractorFactory.create(
- repository = powerRepository
- ).powerInteractor
-
- underTest = LightRevealScrimRepositoryImpl(
- fakeKeyguardRepository,
- context,
- powerInteractor,
- )
+ powerInteractor =
+ PowerInteractorFactory.create(repository = powerRepository).powerInteractor
+
+ underTest =
+ LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context, powerInteractor, mock())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index 5eab2fc73fe6..df52265384fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -84,8 +84,8 @@ class BurnInInteractorTest : SysuiTestCase() {
@Test
fun udfpsBurnInOffset_updatesOnResolutionScaleChange() =
testScope.runTest {
- val udfpsBurnInOffsetX by collectLastValue(underTest.udfpsBurnInXOffset)
- val udfpsBurnInOffsetY by collectLastValue(underTest.udfpsBurnInYOffset)
+ val udfpsBurnInOffsetX by collectLastValue(underTest.deviceEntryIconXOffset)
+ val udfpsBurnInOffsetY by collectLastValue(underTest.deviceEntryIconYOffset)
assertThat(udfpsBurnInOffsetX).isEqualTo(burnInOffset)
assertThat(udfpsBurnInOffsetY).isEqualTo(burnInOffset)
@@ -101,7 +101,7 @@ class BurnInInteractorTest : SysuiTestCase() {
@Test
fun udfpsBurnInProgress_updatesOnDozeTimeTick() =
testScope.runTest {
- val udfpsBurnInProgress by collectLastValue(underTest.udfpsBurnInProgress)
+ val udfpsBurnInProgress by collectLastValue(underTest.udfpsProgress)
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
setBurnInProgress(.88f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b439fcf8c98a..722c11d9f34c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index 7fb0dd55eef2..49f7565517da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
@@ -413,6 +413,13 @@ class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() {
)
transitionRepository.sendTransitionStep(
TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
transitionState = TransitionState.STARTED,
from = KeyguardState.GONE,
to = KeyguardState.AOD,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index e45f56a44b67..f2de8caef176 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -30,6 +30,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -153,9 +154,11 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
bouncerRepository,
+ FakeFingerprintPropertyRepository(),
fakeBiometricSettingsRepository,
FakeSystemClock(),
keyguardUpdateMonitor,
+ testScope.backgroundScope,
),
keyguardTransitionInteractor,
featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 29b546bd49ad..4f7d9444020c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -26,13 +26,14 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
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.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertEquals
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -136,7 +137,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
@Test
fun startedKeyguardStateTests() = testScope.runTest {
- val finishedSteps by collectValues(underTest.startedKeyguardState)
+ val startedStates by collectValues(underTest.startedKeyguardState)
runCurrent()
val steps = mutableListOf<TransitionStep>()
@@ -153,7 +154,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
runCurrent()
}
- assertThat(finishedSteps).isEqualTo(listOf(OFF, PRIMARY_BOUNCER, AOD, GONE))
+ assertThat(startedStates).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD, GONE))
}
@Test
@@ -162,12 +163,12 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
steps.forEach {
@@ -175,7 +176,9 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
runCurrent()
}
- assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5]))
+ // Ignore the default state.
+ assertThat(finishedSteps.subList(1, finishedSteps.size))
+ .isEqualTo(listOf(steps[2], steps[5]))
}
@Test
@@ -650,6 +653,81 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
))
}
+ @Test
+ fun finishedKeyguardState_emitsAgainIfCancelledAndReversed() = testScope.runTest {
+ val finishedStates by collectValues(underTest.finishedKeyguardState)
+
+ // We default FINISHED in LOCKSCREEN.
+ assertEquals(listOf(
+ LOCKSCREEN
+ ), finishedStates)
+
+ sendSteps(
+ TransitionStep(LOCKSCREEN, AOD, 0f, STARTED),
+ TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING),
+ TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED),
+ )
+
+ // We're FINISHED in AOD.
+ assertEquals(listOf(
+ LOCKSCREEN,
+ AOD,
+ ), finishedStates)
+
+ // Transition back to LOCKSCREEN.
+ sendSteps(
+ TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
+ TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
+ )
+
+ // We're FINISHED in LOCKSCREEN.
+ assertEquals(listOf(
+ LOCKSCREEN,
+ AOD,
+ LOCKSCREEN,
+ ), finishedStates)
+
+ sendSteps(
+ TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
+ TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING),
+ )
+
+ // We've STARTED a transition to GONE but not yet finished it so we're still FINISHED in
+ // LOCKSCREEN.
+ assertEquals(listOf(
+ LOCKSCREEN,
+ AOD,
+ LOCKSCREEN,
+ ), finishedStates)
+
+ sendSteps(
+ TransitionStep(LOCKSCREEN, GONE, 0.6f, CANCELED),
+ )
+
+ // We've CANCELED a transition to GONE, we're still FINISHED in LOCKSCREEN.
+ assertEquals(listOf(
+ LOCKSCREEN,
+ AOD,
+ LOCKSCREEN,
+ ), finishedStates)
+
+ sendSteps(
+ TransitionStep(GONE, LOCKSCREEN, 0.6f, STARTED),
+ TransitionStep(GONE, LOCKSCREEN, 0.9f, RUNNING),
+ TransitionStep(GONE, LOCKSCREEN, 1f, FINISHED),
+ )
+
+ // Expect another emission of LOCKSCREEN, as we have FINISHED a second transition to
+ // LOCKSCREEN after the cancellation.
+ assertEquals(listOf(
+ LOCKSCREEN,
+ AOD,
+ LOCKSCREEN,
+ LOCKSCREEN,
+ ), finishedStates)
+ }
+
private suspend fun sendSteps(vararg steps: TransitionStep) {
steps.forEach {
repository.sendTransitionStep(it)
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 c29210270caf..bf23bf875ad3 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
@@ -618,11 +618,26 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
@Test
fun dozingToLockscreenCannotBeInterruptedByDreaming() =
testScope.runTest {
+ transitionRepository.sendTransitionSteps(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.DOZING,
+ testScheduler
+ )
// GIVEN a prior transition has started to LOCKSCREEN
transitionRepository.sendTransitionStep(
TransitionStep(
from = KeyguardState.DOZING,
to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "KeyguardTransitionScenariosTest",
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
value = 0.5f,
transitionState = TransitionState.RUNNING,
ownerName = "KeyguardTransitionScenariosTest",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index c02add1d0ecb..b483085cf1e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -39,6 +40,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Spy
@@ -79,7 +81,8 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
LightRevealScrimInteractor(
keyguardTransitionInteractor,
fakeLightRevealScrimRepository,
- testScope.backgroundScope
+ testScope.backgroundScope,
+ mock()
)
}
@@ -120,6 +123,9 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
@Test
fun lightRevealEffect_startsAnimationOnlyForDifferentStateTargets() =
testScope.runTest {
+ runCurrent()
+ reset(fakeLightRevealScrimRepository)
+
fakeKeyguardTransitionRepository.sendTransitionStep(
TransitionStep(
transitionState = TransitionState.STARTED,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index f9362a773fc5..91e17059bb3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -153,9 +153,11 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() {
statusBarStateController = mock(),
keyguardStateController = mock(),
bouncerRepository,
+ FakeFingerprintPropertyRepository(),
biometricSettingsRepository,
FakeSystemClock(),
keyguardUpdateMonitor,
+ scope = testScope.backgroundScope,
),
testScope.backgroundScope,
mockedContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 3442df62a441..16d072e99964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -80,11 +80,7 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
configRepository = FakeConfigurationRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
keyguardInteractor = it.keyguardInteractor
keyguardRepository = it.repository
@@ -123,30 +119,30 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
runCurrent()
// THEN burn in offsets are 0
- assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
- assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
- assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
+ assertThat(burnInOffsets?.progress).isEqualTo(0f)
+ assertThat(burnInOffsets?.y).isEqualTo(0)
+ assertThat(burnInOffsets?.x).isEqualTo(0)
// WHEN we're in the middle of the doze amount change
keyguardRepository.setDozeAmount(.50f)
runCurrent()
// THEN burn in is updated (between 0 and the full offset)
- assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
- assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
- assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
- assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
- assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
- assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
+ assertThat(burnInOffsets?.progress).isGreaterThan(0f)
+ assertThat(burnInOffsets?.y).isGreaterThan(0)
+ assertThat(burnInOffsets?.x).isGreaterThan(0)
+ assertThat(burnInOffsets?.progress).isLessThan(burnInProgress)
+ assertThat(burnInOffsets?.y).isLessThan(burnInYOffset)
+ assertThat(burnInOffsets?.x).isLessThan(burnInXOffset)
// WHEN we're fully dozing
keyguardRepository.setDozeAmount(1f)
runCurrent()
// THEN burn in offsets are updated to final current values (for the given time)
- assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
- assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
- assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
+ assertThat(burnInOffsets?.progress).isEqualTo(burnInProgress)
+ assertThat(burnInOffsets?.y).isEqualTo(burnInYOffset)
+ assertThat(burnInOffsets?.x).isEqualTo(burnInXOffset)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 570dfb3f0a9e..e9399cc17158 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
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 43d70adf26b0..15a17827a603 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
@@ -30,7 +30,6 @@ import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.items.ClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
@@ -41,6 +40,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSe
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
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -59,8 +59,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
@Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
@Mock private lateinit var mDefaultDeviceEntryIconSection: DefaultDeviceEntryIconSection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
- @Mock
- private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
+ @Mock private lateinit var defaultAmbientIndicationAreaSection: Optional<KeyguardSection>
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
@Mock private lateinit var defaultStatusBarViewSection: DefaultStatusBarSection
@@ -98,7 +97,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
fun replaceViews() {
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(null, constraintLayout)
- underTest.sections.forEach { verify(it).addViews(constraintLayout) }
+ underTest.sections.forEach { verify(it)?.addViews(constraintLayout) }
}
@Test
@@ -110,7 +109,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(prevBlueprint, constraintLayout)
underTest.sections.minus(mDefaultDeviceEntryIconSection).forEach {
- verify(it, never()).addViews(constraintLayout)
+ verify(it, never())?.addViews(constraintLayout)
}
verify(mDefaultDeviceEntryIconSection).addViews(constraintLayout)
@@ -118,9 +117,16 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
}
@Test
+ fun deviceEntryIconIsOnTop() {
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.replaceViews(null, constraintLayout)
+ underTest.sections.forEach { verify(it)?.addViews(constraintLayout) }
+ }
+
+ @Test
fun applyConstraints() {
val cs = ConstraintSet()
underTest.applyConstraints(cs)
- underTest.sections.forEach { verify(it).applyConstraints(cs) }
+ underTest.sections.forEach { verify(it)?.applyConstraints(cs) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 71313c8a5bf3..d9760456bcef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -24,24 +24,30 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
@@ -64,10 +70,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
- set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
- }
+ FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) }
underTest =
DefaultDeviceEntryIconSection(
keyguardUpdateMonitor,
@@ -77,8 +80,13 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
notificationPanelView,
featureFlags,
{ lockIconViewController },
- { DeviceEntryIconViewModel() },
+ { mock(DeviceEntryIconViewModel::class.java) },
+ { mock(DeviceEntryForegroundViewModel::class.java) },
+ { mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
+ { mock(AlternateBouncerViewModel::class.java) },
+ { mock(NotificationShadeWindowController::class.java) },
+ TestScope().backgroundScope,
)
}
@@ -93,7 +101,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun addViewsConditionally_migrateAndRefactorFlagsOn() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -102,7 +110,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun addViewsConditionally_migrateFlagOff() {
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isEqualTo(0)
@@ -110,7 +118,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun applyConstraints_udfps_refactor_off() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.applyConstraints(cs)
@@ -122,7 +130,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun applyConstraints_udfps_refactor_on() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.applyConstraints(cs)
@@ -134,7 +142,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun testCenterIcon_udfps_refactor_off() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.centerIcon(Point(5, 6), 1F, cs)
@@ -150,7 +158,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
@Test
fun testCenterIcon_udfps_refactor_on() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.centerIcon(Point(5, 6), 1F, cs)
@@ -163,4 +171,21 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() {
assertThat(constraint.layout.topMargin).isEqualTo(5)
assertThat(constraint.layout.startMargin).isEqualTo(4)
}
+
+ @Test
+ fun deviceEntryIconViewIsAboveAlternateBouncerView() {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ assertThat(constraintLayout.childCount).isGreaterThan(0)
+ val deviceEntryIconView = constraintLayout.getViewById(R.id.device_entry_icon_view)
+ val alternateBouncerView = constraintLayout.getViewById(R.id.alternate_bouncer)
+ assertThat(deviceEntryIconView).isNotNull()
+ assertThat(alternateBouncerView).isNotNull()
+
+ // device entry icon is above the alternate bouncer
+ assertThat(constraintLayout.indexOfChild(deviceEntryIconView))
+ .isGreaterThan(constraintLayout.indexOfChild(alternateBouncerView))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
new file mode 100644
index 000000000000..f282481ba01c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.ui.viewmodel
+
+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.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToGoneTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToGoneTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest = AodToGoneTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 000000000000..517149c99a62
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ underTest =
+ AodToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewShows() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_udfps_alphaFadeIn() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // fade in
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.2f)
+
+ repository.sendTransitionStep(step(0.3f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.6f)
+
+ repository.sendTransitionStep(step(0.6f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_rearFp_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.3f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.6f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
new file mode 100644
index 000000000000..96f69462accf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.ui.viewmodel
+
+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.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToOccludedTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToOccludedTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest = AodToOccludedTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { Truth.assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.OCCLUDED,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToOccludedTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 000000000000..5dccc3b1d05f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.ui.viewmodel
+
+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.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var testScope: TestScope
+ private lateinit var underTest: DozingToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ underTest =
+ DozingToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewShows() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "DozingToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index 6d47aed58dac..fd125e099f1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,12 @@ 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.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -35,6 +41,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -44,22 +51,34 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
val interactor =
KeyguardTransitionInteractorFactory.create(
scope = TestScope().backgroundScope,
repository = repository,
)
.keyguardTransitionInteractor
- underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
+ underTest =
+ DreamingToLockscreenTransitionViewModel(
+ interactor,
+ mock(),
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ ),
+ )
}
@Test
@@ -129,6 +148,78 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ fun deviceEntryParentViewFadeIn() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAppear() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+ sensorLocations = emptyMap(),
+ )
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ values.forEach { assertThat(it).isEqualTo(1f) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackground_noUdfps_noUpdates() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.REAR,
+ sensorLocations = emptyMap(),
+ )
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(0) // no updates
+
+ job.cancel()
+ }
+
+ @Test
fun lockscreenTranslationY() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
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 255f4df17244..c1444a55f7d9 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
@@ -19,7 +19,11 @@ 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.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -27,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.collect.Range
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.runTest
@@ -34,11 +39,14 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class GoneToAodTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: GoneToAodTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var testScope: TestScope
@Before
@@ -47,13 +55,24 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
testScope = TestScope(testDispatcher)
repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = GoneToAodTransitionViewModel(interactor)
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ GoneToAodTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
}
@Test
@@ -63,11 +82,11 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
val enterFromTopTranslationY by
collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
- // The animation should only start > halfway through
+ // The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(enterFromTopTranslationY).isEqualTo(pixels)
- repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.4f))
assertThat(enterFromTopTranslationY).isEqualTo(pixels)
repository.sendTransitionStep(step(.85f))
@@ -83,11 +102,11 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
testScope.runTest {
val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
- // The animation should only start > halfway through
+ // The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
- repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.4f))
assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
repository.sendTransitionStep(step(.85f))
@@ -97,6 +116,98 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
assertThat(enterFromTopAnimationAlpha).isEqualTo(1f)
}
+ @Test
+ fun deviceEntryBackgroundViewAlpha() =
+ testScope.runTest {
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.01f, 1f))
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.99f, 1f))
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 259c74ff25fa..a838684a4f41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -21,18 +21,17 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -46,6 +45,8 @@ import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.ClockController
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -64,7 +65,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
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.Test
@@ -109,10 +109,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- val featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FACE_AUTH_REFACTOR, true)
- }
+ val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
keyguardInteractor = withDeps.keyguardInteractor
@@ -351,10 +348,7 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {
.create(
test = this,
featureFlags =
- FakeFeatureFlagsClassicModule {
- setDefault(Flags.NEW_AOD_TRANSITION)
- set(Flags.FACE_AUTH_REFACTOR, true)
- },
+ FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, true) },
mocks =
TestMocksModule(
dozeParameters = dozeParams,
@@ -367,6 +361,11 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {
block()
}
+ @Before
+ fun before() {
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
+ }
+
@Test
fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = runTest {
val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 7de28de4d436..d07836d3abce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -19,7 +19,7 @@ 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.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -45,7 +45,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.deviceEntryRepository.setUnlocked(true)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
@@ -94,6 +94,8 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
KeyguardLongPressViewModel(
interactor = mock(),
),
+ keyguardRoot = utils.keyguardRootViewModel(),
+ notifications = utils.notificationsPlaceholderViewModel(),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
new file mode 100644
index 000000000000..c50be04e8a9c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToAodTransitionViewModelTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToAodTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val deviceEntryRepository: FakeDeviceEntryRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+ val fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ val biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerLockscreenToAodTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(FACE_AUTH_REFACTOR, true)
+ set(FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+
+ @Test
+ fun backgroundViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading out before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun backgroundViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeNotExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ shadeExpanded(false)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.3f),
+ step(.7f),
+ step(1f),
+ ),
+ testScope = testScope,
+ )
+ // immediately 1f
+ values.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ shadeExpanded(true)
+ runCurrent()
+
+ // fade in
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading in before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFp_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsRearFps()
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading out before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFp_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsRearFps()
+ shadeExpanded(true)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.3f),
+ step(.7f),
+ step(1f),
+ ),
+ testScope = testScope,
+ )
+ // immediately 0f
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 89a1d2b3011d..26704da1496f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,88 +18,171 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import dagger.BindsInstance
+import dagger.Component
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
- private lateinit var underTest: LockscreenToDreamingTransitionViewModel
- private lateinit var repository: FakeKeyguardTransitionRepository
-
- @Before
- fun setUp() {
- repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = LockscreenToDreamingTransitionViewModel(interactor)
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToDreamingTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
}
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerLockscreenToDreamingTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
@Test
fun lockscreenFadeOut() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
- val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
-
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.1f))
- repository.sendTransitionStep(step(0.2f))
- repository.sendTransitionStep(step(0.3f))
- // ...up to here
- repository.sendTransitionStep(step(1f))
+ testComponent.runTest {
+ val values by collectValues(underTest.lockscreenAlpha)
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.1f),
+ step(.2f),
+ step(.3f), // ...up to here
+ step(1f),
+ ),
+ testScope = testScope,
+ )
// Only three values should be present, since the dream overlay runs for a small
// fraction of the overall animation time
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationY() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.5f))
- repository.sendTransitionStep(step(1f))
- // And a final reset event on FINISHED
- repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f),
+ step(1f, TransitionState.FINISHED), // Final reset event on FINISHED
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(6)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
// Validate finished value
assertThat(values[5]).isEqualTo(0f)
+ }
- job.cancel()
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f),
+ step(1f, TransitionState.FINISHED),
+ ),
+ testScope = testScope,
+ )
+
+ // immediately 0f
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
}
private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
new file mode 100644
index 000000000000..1494c92cdb06
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.ui.viewmodel
+
+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.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToGoneTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: LockscreenToGoneTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest =
+ LockscreenToGoneTransitionViewModel(
+ interactor,
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 41f8856d5ca2..ff3135a6ad98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,109 +18,185 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import dagger.BindsInstance
+import dagger.Component
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
- private lateinit var underTest: LockscreenToOccludedTransitionViewModel
- private lateinit var repository: FakeKeyguardTransitionRepository
-
- @Before
- fun setUp() {
- repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = LockscreenToOccludedTransitionViewModel(interactor)
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToOccludedTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
}
- @Test
- fun lockscreenFadeOut() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
- val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.1f))
- repository.sendTransitionStep(step(0.4f))
- repository.sendTransitionStep(step(0.7f))
- // ...up to here
- repository.sendTransitionStep(step(1f))
+ private val testComponent: TestComponent =
+ DaggerLockscreenToOccludedTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+ @Test
+ fun lockscreenFadeOut() =
+ testComponent.runTest {
+ val values by collectValues(underTest.lockscreenAlpha)
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.1f),
+ step(.4f),
+ step(.7f), // ...up to here
+ step(1f),
+ ),
+ testScope = testScope,
+ )
// Only 3 values should be present, since the dream overlay runs for a small fraction
// of the overall animation time
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationY() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.5f))
- repository.sendTransitionStep(step(1f))
- // ...up to here
-
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f), // ...up to here
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationYIsCanceled() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.3f, TransitionState.CANCELED))
-
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(.3f),
+ step(0.3f, TransitionState.CANCELED),
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
// Cancel will reset the translation
assertThat(values[3]).isEqualTo(0)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.5f),
+ step(1f, TransitionState.FINISHED)
+ ),
+ testScope = testScope,
+ )
+
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.2f))
+ assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ assertThat(actual).isEqualTo(0f)
- job.cancel()
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
}
private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
new file mode 100644
index 000000000000..8afd8e4fd425
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToPrimaryBouncerTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerLockscreenToPrimaryBouncerTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.2f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.8f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ runCurrent()
+ Truth.assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING,
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
new file mode 100644
index 000000000000..0eb8ff6ba966
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludedToAodTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: OccludedToAodTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ OccludedToAodTransitionViewModel(
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha() = runTest {
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "OccludedToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index ec95cb8c43f1..d0772270ed5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,10 @@ 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.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -26,6 +30,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -35,22 +40,35 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: OccludedToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = OccludedToLockscreenTransitionViewModel(interactor)
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ underTest =
+ OccludedToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
}
@Test
@@ -113,6 +131,78 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun deviceEntryParentViewFadeIn() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ // Should start running here...
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ // ...up to here
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(5)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewShows() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ values.forEach { assertThat(it).isEqualTo(1f) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_noUdfpsEnrolled_noUpdates() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values).isEmpty() // no updates
+
+ job.cancel()
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
new file mode 100644
index 000000000000..350b31008478
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -0,0 +1,164 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: PrimaryBouncerToAodTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest =
+ PrimaryBouncerToAodTransitionViewModel(
+ interactor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_fadeIn() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(.75f))
+ repository.sendTransitionStep(step(1f))
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.75f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 000000000000..24e4920c66d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: PrimaryBouncerToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ PrimaryBouncerToLockscreenTransitionViewModel(
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha() = runTest {
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.5f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.75f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(bgViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 32acefebfa68..5058b1686781 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -67,11 +67,7 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
testScope = TestScope()
shadeRepository = FakeShadeRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(
featureFlags = featureFlags,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 4f970d708425..f039f5302a3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -75,11 +75,7 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
fakeCommandQueue = FakeCommandQueue()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
bouncerRepository = FakeKeyguardBouncerRepository()
transitionRepository = FakeKeyguardTransitionRepository()
shadeRepository = FakeShadeRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index 30e48669205f..c1805dbf26ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -83,11 +83,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
testScope = TestScope()
transitionRepository = FakeKeyguardTransitionRepository()
shadeRepository = FakeShadeRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(
featureFlags = featureFlags,
)
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/pipeline/MediaDeviceManagerTest.kt
index b101acf3418b..437a35f2fab5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -38,8 +38,6 @@ 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.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
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
@@ -112,7 +110,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
- private val featureFlags = FakeFeatureFlagsClassic()
@Before
fun setUp() {
@@ -131,7 +128,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fakeFgExecutor,
fakeBgExecutor,
dumpster,
- featureFlags,
)
manager.addListener(listener)
@@ -150,7 +146,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, false)
}
@After
@@ -463,7 +458,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session name is null, and is a system session for a PhoneMediaDevice
val phoneDevice = mock(PhoneMediaDevice::class.java)
whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
@@ -489,7 +483,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session does not have a name, and is a system session
whenever(route.name).thenReturn(null)
whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
@@ -725,101 +718,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
assertThat(data.showBroadcastButton).isFalse()
}
- // Duplicates of above tests with MEDIA_DEVICE_NAME_FIX enabled
-
- @Test
- fun loadMediaDataWithNullToken_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- }
-
- @Test
- fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData)
- // Run and reset the executors and listeners so we only focus on new events.
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- reset(listener)
-
- // Ensure we'll get device info when using the address
- val fullMediaDevice = mock(MediaDevice::class.java)
- val address = "fakeAddress"
- val nameFromDevice = "nameFromDevice"
- val iconFromDevice = mock(Drawable::class.java)
- whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
- whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
- whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
-
- // WHEN the about-to-connect device changes to non-null
- val deviceCallback = captureCallback()
- val nameFromParam = "nameFromParam"
- val iconFromParam = mock(Drawable::class.java)
- deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
-
- // THEN the about-to-connect device based on the address is returned
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(nameFromDevice)
- assertThat(data.name).isNotEqualTo(nameFromParam)
- assertThat(data.icon).isEqualTo(iconFromDevice)
- assertThat(data.icon).isNotEqualTo(iconFromParam)
- }
-
- @Test
- fun deviceNameFromMR2RouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a valid routing session
- whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN it uses the route name (instead of device name)
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
- }
-
- @Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns null for routing session
- whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is disabled and name is set to null
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- }
-
- @Test
- fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a routing session that does not have a name
- whenever(route.name).thenReturn(null)
- whenever(route.isSystemSession).thenReturn(false)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is enabled and uses the current connected device name
- val data = captureDeviceData(KEY)
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- assertThat(data.enabled).isTrue()
- }
-
- // End duplicate tests
-
private fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
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/MediaHierarchyManagerTest.kt
index a2eb5ef9e463..db7c9876f21b 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/MediaHierarchyManagerTest.kt
@@ -35,7 +35,7 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -50,6 +50,7 @@ import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
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
@@ -74,7 +75,7 @@ import org.mockito.junit.MockitoJUnit
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MediaHierarchyManagerTest : SysuiTestCase() {
@Mock private lateinit var lockHost: MediaHost
@@ -91,6 +92,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var logger: MediaViewLogger
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@@ -101,12 +103,12 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
ArgumentCaptor<(DreamOverlayStateController.Callback)>
@JvmField @Rule val mockito = MockitoJUnit.rule()
private lateinit var mediaHierarchyManager: MediaHierarchyManager
+ private lateinit var isQsBypassingShade: MutableStateFlow<Boolean>
private lateinit var mediaFrame: ViewGroup
private val configurationController = FakeConfigurationController()
private val communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
private val communalInteractor =
CommunalInteractorFactory.create(communalRepository = communalRepository).communalInteractor
- private val notifPanelEvents = ShadeExpansionStateManager()
private val settings = FakeSettings()
private lateinit var testableLooper: TestableLooper
private lateinit var fakeHandler: FakeHandler
@@ -122,6 +124,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
fakeHandler = FakeHandler(testableLooper.looper)
whenever(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
+ isQsBypassingShade = MutableStateFlow(false)
+ whenever(shadeInteractor.isQsBypassingShade).thenReturn(isQsBypassingShade)
mediaHierarchyManager =
MediaHierarchyManager(
context,
@@ -135,7 +139,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
communalInteractor,
configurationController,
wakefulnessLifecycle,
- notifPanelEvents,
+ shadeInteractor,
settings,
fakeHandler,
testScope.backgroundScope,
@@ -430,17 +434,21 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isTrue()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun isCurrentlyInGuidedTransformation_hostsVisible_expandImmediateEnabled_returnsFalse() {
- notifPanelEvents.notifyExpandImmediateChange(true)
- goToLockscreen()
- enterGuidedTransformation()
- whenever(lockHost.visible).thenReturn(true)
- whenever(qsHost.visible).thenReturn(true)
- whenever(qqsHost.visible).thenReturn(true)
+ fun isCurrentlyInGuidedTransformation_hostsVisible_expandImmediateEnabled_returnsFalse() =
+ testScope.runTest {
+ runCurrent()
+ isQsBypassingShade.value = true
+ runCurrent()
+ goToLockscreen()
+ enterGuidedTransformation()
+ whenever(lockHost.visible).thenReturn(true)
+ whenever(qsHost.visible).thenReturn(true)
+ whenever(qqsHost.visible).thenReturn(true)
- assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
- }
+ assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
+ }
@Test
fun isCurrentlyInGuidedTransformation_hostNotVisible_returnsFalse_with_active() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 83145bd8bee8..3be50b1baedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -35,15 +35,13 @@ import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -113,7 +111,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
private lateinit var uiEventLogger: MediaTttSenderUiEventLogger
private lateinit var tempViewUiEventLogger: TemporaryViewUiEventLogger
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
- private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -163,9 +160,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
fakeWakeLockBuilder,
fakeClock,
tempViewUiEventLogger,
- featureFlags
)
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
chipbarCoordinator.start()
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
new file mode 100644
index 000000000000..db9e548e74c8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
@@ -0,0 +1,86 @@
+package com.android.systemui.qs
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.KeyEvent
+import android.view.View
+import android.widget.Scroller
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PagedTileLayoutTest : SysuiTestCase() {
+
+ @Mock private lateinit var pageIndicator: PageIndicator
+ @Captor private lateinit var captor: ArgumentCaptor<View.OnKeyListener>
+
+ private lateinit var pageTileLayout: TestPagedTileLayout
+ private lateinit var scroller: Scroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ pageTileLayout = TestPagedTileLayout(mContext)
+ pageTileLayout.setPageIndicator(pageIndicator)
+ verify(pageIndicator).setOnKeyListener(captor.capture())
+ setViewWidth(pageTileLayout, width = PAGE_WIDTH)
+ scroller = pageTileLayout.mScroller
+ }
+
+ private fun setViewWidth(view: View, width: Int) {
+ view.left = 0
+ view.right = width
+ }
+
+ @Test
+ fun scrollsRight_afterRightArrowPressed_whenFocusOnPagerIndicator() {
+ pageTileLayout.currentPageIndex = 0
+
+ sendUpEvent(KeyEvent.KEYCODE_DPAD_RIGHT)
+
+ assertThat(scroller.isFinished).isFalse() // aka we're scrolling
+ assertThat(scroller.finalX).isEqualTo(scroller.currX + PAGE_WIDTH)
+ }
+
+ @Test
+ fun scrollsLeft_afterLeftArrowPressed_whenFocusOnPagerIndicator() {
+ pageTileLayout.currentPageIndex = 1 // we won't scroll left if we're on the first page
+
+ sendUpEvent(KeyEvent.KEYCODE_DPAD_LEFT)
+
+ assertThat(scroller.isFinished).isFalse() // aka we're scrolling
+ assertThat(scroller.finalX).isEqualTo(scroller.currX - PAGE_WIDTH)
+ }
+
+ private fun sendUpEvent(keyCode: Int) {
+ val event = KeyEvent(KeyEvent.ACTION_UP, keyCode)
+ captor.value.onKey(pageIndicator, keyCode, event)
+ }
+
+ /**
+ * Custom PagedTileLayout to easy mock "currentItem" i.e. currently visible page. Setting this
+ * up otherwise would require setting adapter etc
+ */
+ class TestPagedTileLayout(context: Context) : PagedTileLayout(context, null) {
+
+ var currentPageIndex: Int = 0
+
+ override fun getCurrentItem(): Int {
+ return currentPageIndex
+ }
+ }
+
+ companion object {
+ const val PAGE_WIDTH = 200
+ }
+}
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 5e4c954a0b26..1f7a02962ce2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -16,6 +16,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.settings.brightness.BrightnessController
import com.android.systemui.settings.brightness.BrightnessSliderController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -61,6 +62,8 @@ class QSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var configuration: Configuration
@Mock private lateinit var pagedTileLayout: PagedTileLayout
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+
private lateinit var controller: QSPanelController
private val testableResources: TestableResources = mContext.orCreateTestableResources
@@ -96,7 +99,8 @@ class QSPanelControllerTest : SysuiTestCase() {
brightnessSliderFactory,
falsingManager,
statusBarKeyguardViewManager,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ sceneContainerFlags,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 79411f427f1f..5245b224fa5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.qs;
+import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -25,7 +27,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -48,7 +49,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.CollectionUtils;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.nano.SystemUIProtoDump;
@@ -67,6 +67,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.di.NewQSTileFactory;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -78,8 +79,6 @@ import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -94,6 +93,8 @@ import java.util.concurrent.Executor;
import javax.inject.Provider;
+import dagger.Lazy;
+
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class QSTileHostTest extends SysuiTestCase {
@@ -103,7 +104,6 @@ public class QSTileHostTest extends SysuiTestCase {
ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
private static final String SETTING = QSHost.TILES_SETTING;
-
@Mock
private PluginManager mPluginManager;
@Mock
@@ -146,8 +146,7 @@ public class QSTileHostTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
- mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
+ mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
// TODO(b/299909337): Add test checking the new factory is used when the flag is on
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_TILES, false);
mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags);
@@ -690,17 +689,6 @@ public class QSTileHostTest extends SysuiTestCase {
assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
}
- @Test
- public void testUserChange_flagOn_autoTileManagerNotified() {
- mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true);
- int currentUser = mUserTracker.getUserId();
- clearInvocations(mAutoTiles);
- when(mUserTracker.getUserId()).thenReturn(currentUser + 1);
-
- mQSTileHost.onTuningChanged(SETTING, "a,b");
- verify(mAutoTiles).changeUser(UserHandle.of(currentUser + 1));
- }
-
private SharedPreferences getSharedPreferencesForUser(int user) {
return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 555484cc17f8..8acde3637576 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -42,6 +43,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+
private lateinit var controller: QuickStatusBarHeaderController
@Before
@@ -51,7 +54,11 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
- controller = QuickStatusBarHeaderController(view, quickQSPanelController)
+ controller = QuickStatusBarHeaderController(
+ view,
+ quickQSPanelController,
+ sceneContainerFlags,
+ )
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 6cc52d70611a..fbd63c6bbdae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -403,6 +403,31 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
}
+ @Test
+ public void testNullBindingCallsUnbind() {
+ Context mockContext = mock(Context.class);
+ // Binding has to succeed
+ when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext,
+ mock(IQSService.class),
+ mMockPackageManagerAdapter,
+ mMockBroadcastDispatcher,
+ mTileServiceIntent,
+ mUser,
+ mActivityManager,
+ mExecutor);
+
+ manager.executeSetBindService(true);
+ mExecutor.runAllReady();
+
+ ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+ verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
+
+ captor.getValue().onNullBinding(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ verify(mockContext).unbindService(captor.getValue());
+ }
+
private void mockChangeEnabled(long changeId, boolean enabled) {
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
any(UserHandle.class)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index a89338a38e89..8c896a6a1709 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -24,6 +24,7 @@ import android.os.UserHandle
import android.service.quicksettings.Tile
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.nano.SystemUIProtoDump
@@ -104,8 +105,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true)
- featureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, true)
+ mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
// TODO(b/299909337): Add test checking the new factory is used when the flag is on
featureFlags.set(Flags.QS_PIPELINE_NEW_TILES, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
index 489221e86d0b..2e637084e6dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
@@ -1,61 +1,33 @@
package com.android.systemui.qs.pipeline.shared
+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.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
class QSPipelineFlagsRepositoryTest : SysuiTestCase() {
- companion object {
- @Parameters(
- name =
- """
-WHEN: qs_pipeline_new_host = {0}, qs_pipeline_auto_add = {1}
-THEN: pipelineNewHost = {2}, pipelineAutoAdd = {3}
- """
- )
- @JvmStatic
- fun data(): List<Array<Boolean>> =
- (0 until 4).map { combination ->
- val qs_pipeline_new_host = combination and 0b10 != 0
- val qs_pipeline_auto_add = combination and 0b01 != 0
- arrayOf(
- qs_pipeline_new_host,
- qs_pipeline_auto_add,
- /* pipelineNewHost = */ qs_pipeline_new_host,
- /* pipelineAutoAdd = */ qs_pipeline_new_host && qs_pipeline_auto_add,
- )
- }
- }
- @JvmField @Parameter(0) var qsPipelineNewHostFlag: Boolean = false
- @JvmField @Parameter(1) var qsPipelineAutoAddFlag: Boolean = false
- @JvmField @Parameter(2) var pipelineNewHostExpected: Boolean = false
- @JvmField @Parameter(3) var pipelineAutoAddExpected: Boolean = false
+ private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
+
+ private val underTest = QSPipelineFlagsRepository(fakeFeatureFlagsClassic)
- private val featureFlags = FakeFeatureFlags()
- private val pipelineFlags = QSPipelineFlagsRepository(featureFlags)
+ @Test
+ fun pipelineFlagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_PIPELINE)
- @Before
- fun setUp() {
- featureFlags.apply {
- set(Flags.QS_PIPELINE_NEW_HOST, qsPipelineNewHostFlag)
- set(Flags.QS_PIPELINE_AUTO_ADD, qsPipelineAutoAddFlag)
- }
+ assertThat(underTest.pipelineEnabled).isFalse()
}
@Test
- fun flagLogic() {
- assertThat(pipelineFlags.pipelineHostEnabled).isEqualTo(pipelineNewHostExpected)
- assertThat(pipelineFlags.pipelineAutoAddEnabled).isEqualTo(pipelineAutoAddExpected)
+ fun pipelineFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
+
+ assertThat(underTest.pipelineEnabled).isTrue()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 8f27e4e12d17..92c2d743c262 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -55,12 +55,9 @@ class QSTileLoggerTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer)
+ val tileSpec: TileSpec = TileSpec.create("chatty_tile")
underTest =
- QSTileLogger(
- mapOf("chatty_tile" to chattyLogBuffer),
- logBufferFactory,
- statusBarController
- )
+ QSTileLogger(mapOf(tileSpec to chattyLogBuffer), logBufferFactory, statusBarController)
}
@Test
@@ -133,7 +130,7 @@ class QSTileLoggerTest : SysuiTestCase() {
"sd=null, " +
"svi=None, " +
"enabled=ENABLED, " +
- "a11y=null" +
+ "a11y=android.widget.Switch" +
"], " +
"data=test_data"
)
@@ -157,7 +154,7 @@ class QSTileLoggerTest : SysuiTestCase() {
"sd=null, " +
"svi=None, " +
"enabled=ENABLED, " +
- "a11y=null], " +
+ "a11y=android.widget.Switch], " +
"data=test_data"
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 3b6bfeeb4ca2..3808c7ee926b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -32,6 +32,11 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -67,17 +72,24 @@ class BluetoothTileDialogTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
+ private lateinit var scheduler: TestCoroutineScheduler
+ private lateinit var dispatcher: CoroutineDispatcher
+ private lateinit var testScope: TestScope
private lateinit var icon: Pair<Drawable, String>
private lateinit var bluetoothTileDialog: BluetoothTileDialog
private lateinit var deviceItem: DeviceItem
@Before
fun setUp() {
+ scheduler = TestCoroutineScheduler()
+ dispatcher = UnconfinedTestDispatcher(scheduler)
+ testScope = TestScope(dispatcher)
bluetoothTileDialog =
BluetoothTileDialog(
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -111,29 +123,33 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Test
fun testShowDialog_displayBluetoothDevice() {
- bluetoothTileDialog =
- BluetoothTileDialog(
- ENABLED,
- subtitleResId,
- bluetoothTileDialogCallback,
- fakeSystemClock,
- uiEventLogger,
- logger,
- mContext
+ testScope.runTest {
+ bluetoothTileDialog =
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ dispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ mContext
+ )
+ bluetoothTileDialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = false
)
- bluetoothTileDialog.show()
- bluetoothTileDialog.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = false
- )
- val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(adapter.itemCount).isEqualTo(1)
- assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
- assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
- assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+ assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
+ assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
+ assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ }
}
@Test
@@ -147,6 +163,7 @@ class BluetoothTileDialogTest : SysuiTestCase() {
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -173,6 +190,7 @@ class BluetoothTileDialogTest : SysuiTestCase() {
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -190,33 +208,37 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Test
fun testOnDeviceUpdated_hideSeeAll_showPairNew() {
- bluetoothTileDialog =
- BluetoothTileDialog(
- ENABLED,
- subtitleResId,
- bluetoothTileDialogCallback,
- fakeSystemClock,
- uiEventLogger,
- logger,
- mContext
+ testScope.runTest {
+ bluetoothTileDialog =
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ dispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ mContext
+ )
+ bluetoothTileDialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = true
)
- bluetoothTileDialog.show()
- bluetoothTileDialog.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = true
- )
-
- val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
- val pairNewLayout =
- bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
- val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(seeAllLayout).isNotNull()
- assertThat(seeAllLayout.visibility).isEqualTo(GONE)
- assertThat(pairNewLayout).isNotNull()
- assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
- assertThat(adapter.itemCount).isEqualTo(1)
+ val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
+ val pairNewLayout =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+
+ assertThat(seeAllLayout).isNotNull()
+ assertThat(seeAllLayout.visibility).isEqualTo(GONE)
+ assertThat(pairNewLayout).isNotNull()
+ assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(adapter.itemCount).isEqualTo(1)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
index 682b2d0d3983..5eca8caa7d15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
@@ -19,7 +19,9 @@ package com.android.systemui.qs.tiles.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,8 +31,8 @@ import org.junit.runner.RunWith
class QSTileConfigProviderTest : SysuiTestCase() {
private val underTest =
- QSTileConfigProviderImpl(
- mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
+ createQSTileConfigProviderImpl(
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC }),
)
@Test
@@ -43,13 +45,31 @@ class QSTileConfigProviderTest : SysuiTestCase() {
underTest.getConfig(INVALID_SPEC.spec)
}
+ @Test
+ fun hasConfigReturnsTrueForValidSpec() {
+ assertThat(underTest.hasConfig(VALID_SPEC.spec)).isTrue()
+ }
+
+ @Test
+ fun hasConfigReturnsFalseForInvalidSpec() {
+ assertThat(underTest.hasConfig(INVALID_SPEC.spec)).isFalse()
+ }
+
@Test(expected = IllegalArgumentException::class)
fun validatesSpecUponCreation() {
- QSTileConfigProviderImpl(
+ createQSTileConfigProviderImpl(
mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = INVALID_SPEC })
)
}
+ private fun createQSTileConfigProviderImpl(
+ configs: Map<String, QSTileConfig>
+ ): QSTileConfigProviderImpl =
+ QSTileConfigProviderImpl(
+ configs,
+ mock<QsEventLogger>(),
+ )
+
private companion object {
val VALID_SPEC = TileSpec.create("valid_tile_spec")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
deleted file mode 100644
index d3b7daad792e..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ /dev/null
@@ -1,120 +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.qs.tiles.viewmodel
-
-import android.os.UserHandle
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-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.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-// TODO(b/299909368): Add more tests
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
-
- @Mock private lateinit var qsTileLogger: QSTileLogger
- @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
-
- private val fakeUserRepository = FakeUserRepository()
- private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
- private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
- private val fakeDisabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
- private val fakeFalsingManager = FalsingManagerFake()
-
- private val testCoroutineDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testCoroutineDispatcher)
-
- private lateinit var underTest: QSTileViewModel
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- underTest = createViewModel(testScope)
- }
-
- @Test
- fun testDoesntListenStateUntilCreated() =
- testScope.runTest {
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- underTest.state.launchIn(backgroundScope)
- runCurrent()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
- assertThat(fakeQSTileDataInteractor.dataRequests.first())
- .isEqualTo(FakeQSTileDataInteractor.DataRequest(UserHandle.of(0)))
- }
-
- private fun createViewModel(
- scope: TestScope,
- config: QSTileConfig = TEST_QS_TILE_CONFIG,
- ): QSTileViewModel =
- QSTileViewModelImpl(
- config,
- { fakeQSTileUserActionInteractor },
- { fakeQSTileDataInteractor },
- {
- object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState =
- QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- ""
- ) {}
- }
- },
- fakeDisabledByPolicyInteractor,
- fakeUserRepository,
- fakeFalsingManager,
- qsTileAnalytics,
- qsTileLogger,
- FakeSystemClock(),
- testCoroutineDispatcher,
- scope.backgroundScope,
- )
-
- private companion object {
-
- val TEST_QS_TILE_CONFIG = QSTileConfigTestBuilder.build {}
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
new file mode 100644
index 000000000000..3a0ebdbd6a17
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.viewmodel
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+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.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun stateReceivedForTheData() =
+ testScope.runTest {
+ val testTileData = "test_tile_data"
+ val states = collectValues(underTest.state)
+ runCurrent()
+
+ tileDataInteractor.emitData(testTileData)
+ runCurrent()
+
+ assertThat(states()).isNotEmpty()
+ assertThat(states().first().label).isEqualTo(testTileData)
+ verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun doesntListenDataIfStateIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListenAvailabilityIfAvailabilityIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListedDataAfterDestroy() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.destroy()
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+ }
+
+ @Test
+ fun forceUpdateTriggersData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.forceUpdate()
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.ForceUpdate::class.java)
+ verify(qsTileLogger).logForceUpdate(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun userChangeUpdatesData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.DataRequest(USER))
+ }
+
+ @Test
+ fun userChangeUpdatesAvailability() =
+ testScope.runTest {
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilityRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
+ }
+
+ private fun createViewModel(
+ scope: TestScope,
+ config: QSTileConfig = tileConfig,
+ ): QSTileViewModel =
+ QSTileViewModelImpl(
+ config,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+
+ private companion object {
+
+ val USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
new file mode 100644
index 000000000000..22fb152aee44
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.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.qs.tiles.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+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.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/** Tests all possible [QSTileUserAction]s. If you need */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelUserInputTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ // TODO(b/299909989): this should be parametrised. b/299096521 blocks this.
+ private val userAction: QSTileUserAction = QSTileUserAction.Click(null)
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun userInputTriggersData() =
+ testScope.runTest {
+ tileDataInteractor.emitData("initial_data")
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserAction(eq(userAction), eq(tileConfig.tileSpec), eq(true), eq(true))
+ verify(qsTileLogger)
+ .logUserActionPipeline(
+ eq(tileConfig.tileSpec),
+ eq(userAction),
+ any(),
+ eq("initial_data")
+ )
+ verify(qsTileAnalytics).trackUserAction(eq(tileConfig), eq(userAction))
+ }
+
+ @Test
+ fun disabledByPolicyUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ disabledByPolicyInteractor.policyResult =
+ DisabledByPolicyInteractor.PolicyResult.TileDisabled(
+ RestrictedLockUtils.EnforcedAdmin()
+ )
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByPolicy(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun falsedUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ falsingManager.setFalseLongTap(true)
+ falsingManager.setFalseTap(true)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByFalsing(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun userInputIsThrottled() =
+ testScope.runTest {
+ val inputCount = 100
+ underTest.state.launchIn(backgroundScope)
+
+ repeat(inputCount) { underTest.onActionPerformed(userAction) }
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.size).isLessThan(inputCount)
+ }
+
+ private fun createViewModel(scope: TestScope): QSTileViewModel =
+ QSTileViewModelImpl(
+ tileConfig,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
new file mode 100644
index 000000000000..c1c012613fa1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -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.systemui.qs.ui.adapter
+
+import android.os.Bundle
+import android.view.View
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater
+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.qs.QSImpl
+import com.android.systemui.qs.dagger.QSComponent
+import com.android.systemui.qs.dagger.QSSceneComponent
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+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 javax.inject.Provider
+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.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSSceneAdapterImplTest : SysuiTestCase() {
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private val qsImplProvider =
+ object : Provider<QSImpl> {
+ val impls = mutableListOf<QSImpl>()
+
+ override fun get(): QSImpl {
+ return mock<QSImpl> {
+ lateinit var _view: View
+ whenever(onComponentCreated(any(), any())).then {
+ _view = it.getArgument<QSComponent>(0).getRootView()
+ Unit
+ }
+ whenever(view).thenAnswer { _view }
+ }
+ .also { impls.add(it) }
+ }
+ }
+
+ private val qsSceneComponentFactory =
+ object : QSSceneComponent.Factory {
+ val components = mutableListOf<QSSceneComponent>()
+
+ override fun create(rootView: View): QSSceneComponent {
+ return mock<QSSceneComponent> { whenever(this.getRootView()).thenReturn(rootView) }
+ .also { components.add(it) }
+ }
+ }
+
+ private val mockAsyncLayoutInflater =
+ mock<AsyncLayoutInflater>() {
+ whenever(inflate(anyInt(), nullable(), any())).then { invocation ->
+ val mockView = mock<View>()
+ invocation
+ .getArgument<AsyncLayoutInflater.OnInflateFinishedListener>(2)
+ .onInflateFinished(
+ mockView,
+ invocation.getArgument(0),
+ invocation.getArgument(1),
+ )
+ }
+ }
+
+ private val underTest =
+ QSSceneAdapterImpl(
+ qsSceneComponentFactory,
+ qsImplProvider,
+ testDispatcher,
+ testScope.backgroundScope,
+ { mockAsyncLayoutInflater },
+ )
+
+ @Test
+ fun inflate() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ assertThat(qsImpl).isNull()
+
+ underTest.inflate(context)
+ runCurrent()
+
+ assertThat(qsImpl).isNotNull()
+ assertThat(qsImpl).isSameInstanceAs(qsImplProvider.impls[0])
+ val inOrder = inOrder(qsImpl!!)
+ inOrder.verify(qsImpl!!).onCreate(nullable())
+ inOrder
+ .verify(qsImpl!!)
+ .onComponentCreated(
+ eq(qsSceneComponentFactory.components[0]),
+ any(),
+ )
+ }
+
+ @Test
+ fun initialState_closed() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+
+ with(qsImpl!!) {
+ verify(this).setQsVisible(false)
+ verify(this)
+ .setQsExpansion(
+ /* expansion= */ 0f,
+ /* panelExpansionFraction= */ 1f,
+ /* proposedTranslation= */ 0f,
+ /* squishinessFraction= */ 1f,
+ )
+ verify(this).setListening(false)
+ verify(this).setExpanded(false)
+ verify(this)
+ .setTransitionToFullShadeProgress(
+ /* isTransitioningToFullShade= */ false,
+ /* qsTransitionFraction= */ 1f,
+ /* qsSquishinessFraction = */ 1f,
+ )
+ }
+ }
+
+ @Test
+ fun state_qqs() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+ clearInvocations(qsImpl!!)
+
+ underTest.setState(QSSceneAdapter.State.QQS)
+ with(qsImpl!!) {
+ verify(this).setQsVisible(true)
+ verify(this)
+ .setQsExpansion(
+ /* expansion= */ 0f,
+ /* panelExpansionFraction= */ 1f,
+ /* proposedTranslation= */ 0f,
+ /* squishinessFraction= */ 1f,
+ )
+ verify(this).setListening(true)
+ verify(this).setExpanded(true)
+ verify(this)
+ .setTransitionToFullShadeProgress(
+ /* isTransitioningToFullShade= */ false,
+ /* qsTransitionFraction= */ 1f,
+ /* qsSquishinessFraction = */ 1f,
+ )
+ }
+ }
+
+ @Test
+ fun state_qs() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+ clearInvocations(qsImpl!!)
+
+ underTest.setState(QSSceneAdapter.State.QS)
+ with(qsImpl!!) {
+ verify(this).setQsVisible(true)
+ verify(this)
+ .setQsExpansion(
+ /* expansion= */ 1f,
+ /* panelExpansionFraction= */ 1f,
+ /* proposedTranslation= */ 0f,
+ /* squishinessFraction= */ 1f,
+ )
+ verify(this).setListening(true)
+ verify(this).setExpanded(true)
+ verify(this)
+ .setTransitionToFullShadeProgress(
+ /* isTransitioningToFullShade= */ false,
+ /* qsTransitionFraction= */ 1f,
+ /* qsSquishinessFraction = */ 1f,
+ )
+ }
+ }
+
+ @Test
+ fun customizing_QS() =
+ testScope.runTest {
+ val customizing by collectLastValue(underTest.isCustomizing)
+
+ underTest.inflate(context)
+ runCurrent()
+ underTest.setState(QSSceneAdapter.State.QS)
+
+ assertThat(customizing).isFalse()
+
+ underTest.setCustomizerShowing(true)
+ assertThat(customizing).isTrue()
+
+ underTest.setCustomizerShowing(false)
+ assertThat(customizing).isFalse()
+ }
+
+ @Test
+ fun customizing_moveToQQS_stopCustomizing() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+ underTest.setState(QSSceneAdapter.State.QS)
+ underTest.setCustomizerShowing(true)
+
+ underTest.setState(QSSceneAdapter.State.QQS)
+ runCurrent()
+ verify(qsImpl!!).closeCustomizerImmediately()
+ }
+
+ @Test
+ fun customizing_moveToClosed_stopCustomizing() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+ underTest.setState(QSSceneAdapter.State.QS)
+ underTest.setCustomizerShowing(true)
+ runCurrent()
+
+ underTest.setState(QSSceneAdapter.State.CLOSED)
+ verify(qsImpl!!).closeCustomizerImmediately()
+ }
+
+ @Test
+ fun reinflation_previousStateDestroyed() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+ val oldQsImpl = qsImpl!!
+
+ underTest.inflate(context)
+ runCurrent()
+ val newQSImpl = qsImpl!!
+
+ assertThat(oldQsImpl).isNotSameInstanceAs(newQSImpl)
+ val inOrder = inOrder(oldQsImpl, newQSImpl)
+ val bundleArgCaptor = argumentCaptor<Bundle>()
+
+ inOrder.verify(oldQsImpl).onSaveInstanceState(capture(bundleArgCaptor))
+ inOrder.verify(oldQsImpl).onDestroy()
+ assertThat(newQSImpl).isSameInstanceAs(qsImplProvider.impls[1])
+ inOrder.verify(newQSImpl).onCreate(nullable())
+ inOrder
+ .verify(newQSImpl)
+ .onComponentCreated(
+ qsSceneComponentFactory.components[1],
+ bundleArgCaptor.value,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index a9f82392c7e5..42e27ba12f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -19,13 +19,15 @@ package com.android.systemui.qs.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -36,14 +38,11 @@ import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsPro
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
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.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class QuickSettingsSceneViewModelTest : SysuiTestCase() {
@@ -53,6 +52,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
private val sceneInteractor = utils.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() }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -86,47 +86,40 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
broadcastDispatcher = fakeBroadcastDispatcher,
)
- val authenticationInteractor = utils.authenticationInteractor()
-
underTest =
QuickSettingsSceneViewModel(
- bouncerInteractor =
- utils.bouncerInteractor(
- deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
shadeHeaderViewModel = shadeHeaderViewModel,
+ qsSceneAdapter = qsFlexiglassAdapter,
+ notifications = utils.notificationsPlaceholderViewModel(),
)
}
@Test
- fun onContentClicked_deviceUnlocked_switchesToGone() =
+ fun destinationsNotCustomizing() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ val destinations by collectLastValue(underTest.destinationScenes)
+ qsFlexiglassAdapter.setCustomizing(false)
+
+ assertThat(destinations)
+ .isEqualTo(
+ mapOf(
+ UserAction.Back to SceneModel(SceneKey.Shade),
+ UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
+ )
+ )
}
@Test
- fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
+ fun destinationsCustomizing() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ val destinations by collectLastValue(underTest.destinationScenes)
+ qsFlexiglassAdapter.setCustomizing(true)
+
+ assertThat(destinations)
+ .isEqualTo(
+ mapOf(
+ UserAction.Back to SceneModel(SceneKey.QuickSettings),
+ )
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index e84d274b4763..c3294ff2e26c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -20,13 +20,14 @@ package com.android.systemui.scene
import android.telecom.TelecomManager
import android.telephony.TelephonyManager
+import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
@@ -39,7 +40,7 @@ import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -60,6 +61,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -72,7 +74,6 @@ 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
@@ -135,9 +136,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
@@ -153,6 +152,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
KeyguardLongPressViewModel(
interactor = mock(),
),
+ keyguardRoot = utils.keyguardRootViewModel(),
+ notifications = utils.notificationsPlaceholderViewModel(),
)
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
@@ -185,6 +186,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private var bouncerSceneJob: Job? = null
+ private val qsFlexiglassAdapter = FakeQSSceneAdapter(inflateDelegate = { _, _ -> mock<View>() })
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -234,8 +237,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- bouncerInteractor = bouncerInteractor,
shadeHeaderViewModel = shadeHeaderViewModel,
+ qsSceneAdapter = qsFlexiglassAdapter,
+ notifications = utils.notificationsPlaceholderViewModel(),
)
utils.deviceEntryRepository.setUnlocked(false)
@@ -247,7 +251,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = utils.sceneContainerFlags,
sysUiState = sysUiState,
@@ -255,6 +258,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
sceneLogger = mock(),
falsingCollector = utils.falsingCollector(),
powerInteractor = powerInteractor,
+ bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationInteractor = utils.authenticationInteractor()
)
startable.start()
@@ -298,7 +304,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
@@ -312,7 +318,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to the shade scene.
@@ -329,7 +335,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
@@ -349,7 +356,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.None)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -360,7 +367,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -428,7 +435,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -445,7 +452,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -464,7 +471,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun bouncerActionButtonClick_duringCall_returnsToCall() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
startPhoneCall()
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
@@ -481,6 +488,32 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
verify(telecomManager).showInCallScreen(any())
}
+ @Test
+ fun showBouncer_whenLockedSimIntroduced() =
+ testScope.runTest {
+ setAuthMethod(AuthenticationMethodModel.None)
+ introduceLockedSim()
+ assertCurrentScene(SceneKey.Bouncer)
+ }
+
+ @Test
+ fun goesToGone_whenSimUnlocked_whileDeviceUnlocked() =
+ testScope.runTest {
+ introduceLockedSim()
+ emulateUiSceneTransition(expectedVisible = true)
+ enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.None)
+ assertCurrentScene(SceneKey.Gone)
+ }
+
+ @Test
+ fun showLockscreen_whenSimUnlocked_whileDeviceLocked() =
+ testScope.runTest {
+ introduceLockedSim()
+ emulateUiSceneTransition(expectedVisible = true)
+ enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
+ assertCurrentScene(SceneKey.Lockscreen)
+ }
+
/**
* Asserts that the current scene in the view-model matches what's expected.
*
@@ -508,19 +541,19 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
/** Updates the current authentication method and related states in the data layer. */
private fun TestScope.setAuthMethod(
- authMethod: DomainLayerAuthenticationMethodModel,
+ authMethod: AuthenticationMethodModel,
+ enableLockscreen: Boolean = true
) {
+ if (authMethod.isSecure) {
+ assert(enableLockscreen) {
+ "Lockscreen cannot be disabled with a secure authentication method."
+ }
+ }
// Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
// lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
// is not an observable that can trigger a new evaluation.
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- authMethod !is DomainLayerAuthenticationMethodModel.None
- )
- utils.authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer())
- if (!authMethod.isSecure) {
- // When the auth method is not secure, the device is never considered locked.
- utils.deviceEntryRepository.setUnlocked(true)
- }
+ utils.deviceEntryRepository.setLockscreenEnabled(enableLockscreen)
+ utils.authenticationRepository.setAuthenticationMethod(authMethod)
runCurrent()
}
@@ -648,6 +681,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
+ // This repository state is not changed by the AuthInteractor, it relies on
+ // KeyguardStateController.
+ utils.deviceEntryRepository.setUnlocked(true)
emulateUiSceneTransition(
expectedVisible = false,
)
@@ -678,6 +714,35 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
runCurrent()
}
+ /**
+ * Enters the correct PIN in the sim bouncer UI.
+ *
+ * Asserts that the current scene is [SceneKey.Bouncer] and that the current bouncer UI is a PIN
+ * before proceeding.
+ *
+ * Does not assert that the device is locked or unlocked.
+ */
+ private fun TestScope.enterSimPin(
+ authMethodAfterSimUnlock: AuthenticationMethodModel = AuthenticationMethodModel.None
+ ) {
+ assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
+ .that(getCurrentSceneInUi())
+ .isEqualTo(SceneKey.Bouncer)
+ val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
+ assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
+ .that(authMethodViewModel)
+ .isInstanceOf(PinBouncerViewModel::class.java)
+
+ val pinBouncerViewModel = authMethodViewModel as PinBouncerViewModel
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
+ pinBouncerViewModel.onPinButtonClicked(digit)
+ }
+ pinBouncerViewModel.onAuthenticateButtonClicked()
+ setAuthMethod(authMethodAfterSimUnlock)
+ utils.mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+ }
+
/** Changes device wakefulness state from asleep to awake, going through intermediary states. */
private fun TestScope.wakeUpDevice() {
val wakefulnessModel = powerInteractor.detailedWakefulness.value
@@ -707,7 +772,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
}
/** Emulates the dismissal of the IME (soft keyboard). */
- private fun TestScope.dismissIme(
+ private suspend fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
bouncerViewModel.authMethodViewModel.value?.apply {
@@ -718,4 +783,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
runCurrent()
}
}
+
+ private fun TestScope.introduceLockedSim() {
+ setAuthMethod(AuthenticationMethodModel.Sim)
+ utils.mobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 3f032a45df41..7f4bbbe36768 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -341,6 +341,7 @@ class SceneInteractorTest : SysuiTestCase() {
@Test
fun userInput() =
testScope.runTest {
+ assertThat(utils.powerRepository.userTouchRegistered).isFalse()
underTest.onUserInput()
assertThat(utils.powerRepository.userTouchRegistered).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index f6362fe528ed..c4ec56c906c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -24,16 +24,15 @@ 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.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -63,8 +62,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
private val sceneInteractor = utils.sceneInteractor()
private val sceneContainerFlags = utils.sceneContainerFlags
private val authenticationInteractor = utils.authenticationInteractor()
+ private val bouncerInteractor =
+ utils.bouncerInteractor(authenticationInteractor = authenticationInteractor)
+ private val faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
private val deviceEntryInteractor =
utils.deviceEntryInteractor(
+ faceAuthRepository = faceAuthRepository,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -78,7 +81,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = sceneContainerFlags,
sysUiState = sysUiState,
@@ -86,6 +88,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
sceneLogger = mock(),
falsingCollector = falsingCollector,
powerInteractor = powerInteractor,
+ bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = utils.simBouncerInteractor,
+ authenticationInteractor = authenticationInteractor,
)
@Before
@@ -198,12 +203,55 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
+ // Authenticate using a passive auth method like face auth while bypass is disabled.
+ faceAuthRepository.isAuthenticated.value = true
utils.deviceEntryRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@Test
+ fun stayOnCurrentSceneWhenDeviceIsUnlockedAndUserIsNotOnLockscreen() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ val transitionStateFlowValue =
+ prepareState(
+ isBypassEnabled = true,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ underTest.start()
+ runCurrent()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "switch to shade")
+ transitionStateFlowValue.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+ }
+
+ @Test
+ fun switchToGoneWhenDeviceIsUnlockedAndUserIsOnBouncerWithBypassDisabled() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState(
+ isBypassEnabled = false,
+ initialSceneKey = SceneKey.Bouncer,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ underTest.start()
+
+ // Authenticate using a passive auth method like face auth while bypass is disabled.
+ faceAuthRepository.isAuthenticated.value = true
+ utils.deviceEntryRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceSleepsLocked() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
@@ -255,6 +303,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
prepareState(
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.None,
+ isLockscreenEnabled = false,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -269,7 +318,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
prepareState(
initialSceneKey = SceneKey.Lockscreen,
- authenticationMethod = AuthenticationMethodModel.Swipe,
+ authenticationMethod = AuthenticationMethodModel.None,
+ isLockscreenEnabled = true,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -406,6 +456,24 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun bouncerImeHidden_shouldTransitionBackToLockscreen() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Password,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+
+ bouncerInteractor.onImeHidden()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
testScope.runTest {
utils.keyguardRepository.setAodAvailable(false)
@@ -521,13 +589,77 @@ class SceneContainerStartableTest : SysuiTestCase() {
verify(falsingCollector, times(2)).onBouncerHidden()
}
+ @Test
+ fun switchesToBouncer_whenSimBecomesLocked() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+
+ utils.mobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ }
+
+ @Test
+ fun switchesToLockscreen_whenSimBecomesUnlocked() =
+ testScope.runTest {
+ utils.mobileConnectionsRepository.isAnySimSecure.value = true
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+
+ prepareState(
+ initialSceneKey = SceneKey.Bouncer,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ utils.mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
+ fun switchesToGone_whenSimBecomesUnlocked_ifDeviceUnlockedAndLockscreenDisabled() =
+ testScope.runTest {
+ utils.mobileConnectionsRepository.isAnySimSecure.value = true
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.None,
+ isDeviceUnlocked = true,
+ isLockscreenEnabled = false,
+ )
+ underTest.start()
+ runCurrent()
+ utils.mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
private fun TestScope.prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
initialSceneKey: SceneKey? = null,
authenticationMethod: AuthenticationMethodModel? = null,
+ isLockscreenEnabled: Boolean = true,
startsAwake: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
+ if (authenticationMethod?.isSecure == true) {
+ assert(isLockscreenEnabled) {
+ "Lockscreen cannot be disabled while having a secure authentication method"
+ }
+ }
sceneContainerFlags.enabled = true
utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked)
utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled)
@@ -542,11 +674,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
sceneInteractor.onSceneChanged(SceneModel(it), "reason")
}
authenticationMethod?.let {
- utils.authenticationRepository.setAuthenticationMethod(
- authenticationMethod.toDataLayer()
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- authenticationMethod != AuthenticationMethodModel.None
+ utils.authenticationRepository.setAuthenticationMethod(authenticationMethod)
+ utils.deviceEntryRepository.setLockscreenEnabled(
+ isLockscreenEnabled = isLockscreenEnabled
)
}
if (startsAwake) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 4c8b56227efa..5969bd82c361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.flags.ReleasedFlag
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -68,6 +69,7 @@ internal class SceneContainerFlagsTest(
listOf(
AconfigFlags.FLAG_SCENE_CONTAINER,
AconfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
+ KeyguardShadeMigrationNssl.FLAG_NAME,
)
.forEach { flagToken ->
setFlagsRule.enableFlags(flagToken)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 152be6529464..6e487cdd65b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -25,9 +25,9 @@ import android.view.ViewGroup
import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
@@ -182,5 +182,15 @@ class BrightnessDialogTest : SysuiTestCase() {
brightnessControllerFactory,
mainExecutor,
accessibilityMgr
- )
+ ) {
+ private var finishing = false
+
+ override fun isFinishing(): Boolean {
+ return finishing
+ }
+
+ override fun requestFinish() {
+ finishing = true
+ }
+ }
}
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 bc28ccb3c7d6..03878b7bcf45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -100,6 +100,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto
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.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -122,11 +124,12 @@ import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
import com.android.systemui.scene.SceneTestUtils;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
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.transition.ShadeTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -144,6 +147,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationManag
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -332,7 +336,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
+ @Mock private ActiveNotificationsInteractor mActiveNotificationsInteractor;
@Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
+ @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@@ -371,11 +377,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false);
mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
- mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, false);
mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
- mFeatureFlags.set(Flags.MIGRATE_NSSL, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
- mFeatureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false);
+ mFeatureFlags.set(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT, false);
+
+ mSetFlagsRule.disableFlags(KeyguardShadeMigrationNssl.FLAG_NAME);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
+
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
KeyguardInteractorFactory.create();
@@ -386,26 +394,27 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
StateFlowKt.MutableStateFlow(false));
- mShadeInteractor = new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
new FakeDeviceProvisioningRepository(),
new FakeDisableFlagsRepository(),
mDozeParameters,
- new FakeSceneContainerFlags(),
- mUtils::sceneInteractor,
mFakeKeyguardRepository,
mKeyguardTransitionInteractor,
mPowerInteractor,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
- new SharedNotificationContainerInteractor(
- new FakeConfigurationRepository(),
- mContext,
- new ResourcesSplitShadeStateController()
- ),
- mShadeRepository
+ new ShadeInteractorLegacyImpl(
+ mTestScope.getBackgroundScope(),
+ mFakeKeyguardRepository,
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()
+ ),
+ mShadeRepository
+ )
);
-
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
@@ -422,7 +431,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mDozeParameters,
mScreenOffAnimationController,
mKeyguardLogger,
- mFeatureFlags,
mInteractionJankMonitor,
mKeyguardInteractor,
mKeyguardTransitionInteractor,
@@ -496,6 +504,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
when(mQs.getView()).thenReturn(mView);
when(mQSFragment.getView()).thenReturn(mView);
+ when(mNaturalScrollingSettingObserver.isNaturalScrollingEnabled()).thenReturn(true);
doAnswer(invocation -> {
mFragmentListener = invocation.getArgument(1);
return null;
@@ -702,11 +711,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mKeyguardInteractor,
mActivityStarter,
mSharedNotificationContainerInteractor,
+ mActiveNotificationsInteractor,
mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor,
new ResourcesSplitShadeStateController(),
mPowerInteractor,
- mKeyguardClockPositionAlgorithm);
+ mKeyguardClockPositionAlgorithm,
+ mNaturalScrollingSettingObserver);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
@@ -769,13 +780,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
- mFeatureFlags,
mInteractionJankMonitor,
mShadeLog,
mDumpManager,
mKeyguardFaceAuthInteractor,
mShadeRepository,
mShadeInteractor,
+ mActiveNotificationsInteractor,
mJavaAdapter,
mCastController,
new ResourcesSplitShadeStateController()
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 1a8d4f9bf599..722fb2c75081 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -59,6 +59,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.FaceAuthApiRequestReason;
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;
@@ -793,7 +794,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
// We are interested in the last value of the stack alpha.
ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class);
verify(mNotificationStackScrollLayoutController, atLeastOnce())
- .setAlpha(alphaCaptor.capture());
+ .setMaxAlphaForExpansion(alphaCaptor.capture());
assertThat(alphaCaptor.getValue()).isEqualTo(1.0f);
}
@@ -814,7 +815,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
// We are interested in the last value of the stack alpha.
ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class);
verify(mNotificationStackScrollLayoutController, atLeastOnce())
- .setAlpha(alphaCaptor.capture());
+ .setMaxAlphaForExpansion(alphaCaptor.capture());
assertThat(alphaCaptor.getValue()).isEqualTo(0.0f);
}
@@ -1105,7 +1106,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
public void nsslFlagEnabled_allowOnlyExternalTouches() {
- mFeatureFlags.set(Flags.MIGRATE_NSSL, true);
+ mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME);
// 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/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 7ad84d6186e7..4df7ef533c7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
@@ -26,13 +25,10 @@ import androidx.test.filters.SmallTest
import com.android.internal.util.CollectionUtils
import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -62,9 +58,6 @@ class NotificationPanelViewControllerWithCoroutinesTest :
override fun getMainDispatcher() = Dispatchers.Main.immediate
- private val ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false)
-
@Test
fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest {
launch(Dispatchers.Main.immediate) { givenViewAttached() }
@@ -158,31 +151,8 @@ class NotificationPanelViewControllerWithCoroutinesTest :
}
@Test
- fun doubleTapRequired_onKeyguard_oneWayHapticsDisabled_usesOldVibrate() = runTest {
- launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(KEYGUARD)
-
- listener.onAdditionalTapRequired()
- val packageName = mView.context.packageName
- verify(mKeyguardIndicationController).showTransientIndication(anyInt())
- verify(mVibratorHelper)
- .vibrate(
- any(),
- eq(packageName),
- eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT),
- eq("falsing-additional-tap-required"),
- eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES)
- )
- }
- advanceUntilIdle()
- }
-
- @Test
- fun doubleTapRequired_onKeyguard_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest {
+ fun doubleTapRequired_onKeyguard_usesPerformHapticFeedback() = runTest {
launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
val listener = getFalsingTapListener()
mStatusBarStateController.setState(KEYGUARD)
@@ -208,32 +178,8 @@ class NotificationPanelViewControllerWithCoroutinesTest :
}
@Test
- fun doubleTapRequired_shadeLocked_oneWayHapticsDisabled_usesOldVibrate() = runTest {
- launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- val listener = getFalsingTapListener()
- val packageName = mView.context.packageName
- mStatusBarStateController.setState(SHADE_LOCKED)
-
- listener.onAdditionalTapRequired()
- verify(mVibratorHelper)
- .vibrate(
- any(),
- eq(packageName),
- eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT),
- eq("falsing-additional-tap-required"),
- eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES)
- )
-
- verify(mTapAgainViewController).show()
- }
- advanceUntilIdle()
- }
-
- @Test
- fun doubleTapRequired_shadeLocked_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest {
+ fun doubleTapRequired_shadeLocked_usesPerformHapticFeedback() = runTest {
launch(Dispatchers.Main.immediate) {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
val listener = getFalsingTapListener()
mStatusBarStateController.setState(SHADE_LOCKED)
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 e6cd17f448b9..8403ac59d2f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -51,7 +51,6 @@ import com.android.keyguard.KeyguardSecurityModel;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
-import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
@@ -68,7 +67,6 @@ import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAni
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
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.res.R;
import com.android.systemui.scene.FakeWindowRootViewComponent;
@@ -80,6 +78,8 @@ import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
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.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -128,22 +128,22 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private SysuiColorExtractor mColorExtractor;
- @Mock ColorExtractor.GradientColors mGradientColors;
+ @Mock private ColorExtractor.GradientColors mGradientColors;
@Mock private DumpManager mDumpManager;
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private AuthController mAuthController;
- @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private UserTracker mUserTracker;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+
private final Executor mMainExecutor = MoreExecutors.directExecutor();
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
- private SceneTestUtils mUtils = new SceneTestUtils(this);
- private TestScope mTestScope = mUtils.getTestScope();
+ private final SceneTestUtils mUtils = new SceneTestUtils(this);
+ private final TestScope mTestScope = mUtils.getTestScope();
private ShadeInteractor mShadeInteractor;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -167,11 +167,10 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeShadeRepository shadeRepository = new FakeShadeRepository();
- FakePowerRepository powerRepository = new FakePowerRepository();
- PowerInteractor powerInteractor = new PowerInteractor(
- powerRepository,
- new FalsingCollectorFake(),
+ PowerInteractor powerInteractor = mUtils.powerInteractor(
+ mUtils.getPowerRepository(),
+ mUtils.falsingCollector(),
mScreenOffAnimationController,
mStatusBarStateController);
@@ -180,7 +179,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
new SceneContainerRepository(
mTestScope.getBackgroundScope(),
mUtils.fakeSceneContainerConfig()),
- powerRepository,
+ powerInteractor,
mock(SceneLogger.class));
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
@@ -234,25 +233,26 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mKeyguardSecurityModel,
mSelectedUserInteractor,
powerInteractor);
-
- mShadeInteractor =
- new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
+ mTestScope.getBackgroundScope(),
+ new FakeDeviceProvisioningRepository(),
+ new FakeDisableFlagsRepository(),
+ mock(DozeParameters.class),
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ new FakeUserSetupRepository(),
+ mock(UserSwitcherInteractor.class),
+ new ShadeInteractorLegacyImpl(
mTestScope.getBackgroundScope(),
- new FakeDeviceProvisioningRepository(),
- new FakeDisableFlagsRepository(),
- mock(DozeParameters.class),
- sceneContainerFlags,
- () -> sceneInteractor,
keyguardRepository,
- keyguardTransitionInteractor,
- powerInteractor,
- new FakeUserSetupRepository(),
- mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
new ResourcesSplitShadeStateController()),
- shadeRepository);
+ shadeRepository
+ )
+ );
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
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 5459779121c9..d89491c7b442 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,6 +30,7 @@ import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -51,9 +52,7 @@ import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.ALTERNATE_BOUNCER_VIEW
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.flags.Flags.MIGRATE_NSSL
import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON
@@ -67,6 +66,7 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
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.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
@@ -97,7 +97,6 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -112,8 +111,9 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -198,8 +198,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- featureFlagsClassic.set(MIGRATE_NSSL, false)
- featureFlagsClassic.set(ALTERNATE_BOUNCER_VIEW, false)
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
mCommunalRepository = FakeCommunalRepository()
@@ -468,7 +467,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
// AND the lock icon wants the touch
whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
- featureFlagsClassic.set(MIGRATE_NSSL, true)
+ mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
// THEN touch should NOT be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
@@ -487,7 +486,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
.thenReturn(false)
- featureFlagsClassic.set(MIGRATE_NSSL, true)
+ mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
// THEN touch should NOT be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -506,7 +505,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
.thenReturn(true)
- featureFlagsClassic.set(MIGRATE_NSSL, true)
+ mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
// THEN touch should NOT 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 a6ab6a51bfa7..9c8816c72fae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -29,7 +29,6 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -60,7 +59,6 @@ import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -114,8 +112,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var dozeServiceHost: DozeServiceHost
@Mock private lateinit var dozeScrimController: DozeScrimController
- @Mock private lateinit var backActionInteractor: BackActionInteractor
- @Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
@@ -192,8 +188,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- featureFlags.set(Flags.MIGRATE_NSSL, false)
- featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
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 778cfa67cad7..88a47eb81bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -65,7 +65,10 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-/** Uses Flags.MIGRATE_NSSL set to false. If all goes well, this set of tests will be deleted. */
+/**
+ * Uses Flags.KEYGUARD_STATUS_VIEW_MIGRATE_NSSL set to false. If all goes well, this set of tests
+ * will be deleted.
+ */
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -101,11 +104,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.MIGRATE_NSSL, false)
- set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false) }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
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 23420037e233..1f37ca09dc26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
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,11 +101,8 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.MIGRATE_NSSL, true)
- set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true)
- }
+ mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
+ featureFlags = FakeFeatureFlags().apply { set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true) }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
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 6d0488706133..bff47f1e3927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -37,7 +37,6 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
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.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -58,7 +57,6 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
@@ -70,6 +68,8 @@ import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
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.transition.ShadeTransitionController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -80,6 +80,8 @@ import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
@@ -169,6 +171,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@Mock protected CastController mCastController;
@Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
@Mock protected SelectedUserInteractor mSelectedUserInteractor;
+
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
@@ -177,6 +180,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
protected SysuiStatusBarStateController mStatusBarStateController;
protected ShadeInteractor mShadeInteractor;
+ protected ActiveNotificationsInteractor mActiveNotificationsInteractor;
+
protected Handler mMainHandler;
protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
@@ -198,12 +203,11 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
new FakeDeviceProvisioningRepository();
deviceProvisioningRepository.setDeviceProvisioned(true);
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
- FakePowerRepository powerRepository = new FakePowerRepository();
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
- PowerInteractor powerInteractor = new PowerInteractor(
- powerRepository,
- new FalsingCollectorFake(),
+ PowerInteractor powerInteractor = mUtils.powerInteractor(
+ mUtils.getPowerRepository(),
+ mUtils.falsingCollector(),
mock(ScreenOffAnimationController.class),
mStatusBarStateController);
@@ -212,7 +216,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
new SceneContainerRepository(
mTestScope.getBackgroundScope(),
mUtils.fakeSceneContainerConfig()),
- powerRepository,
+ powerInteractor,
mock(SceneLogger.class));
FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
@@ -251,7 +255,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
new InWindowLauncherUnlockAnimationRepository(),
mTestScope.getBackgroundScope(),
keyguardTransitionInteractor,
- () -> new FakeKeyguardSurfaceBehindRepository(),
+ FakeKeyguardSurfaceBehindRepository::new,
mock(ActivityManagerWrapper.class)
)
);
@@ -269,25 +273,29 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
ResourcesSplitShadeStateController splitShadeStateController =
new ResourcesSplitShadeStateController();
- mShadeInteractor =
- new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
+ mTestScope.getBackgroundScope(),
+ deviceProvisioningRepository,
+ mDisableFlagsRepository,
+ mDozeParameters,
+ mKeyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ new FakeUserSetupRepository(),
+ mUserSwitcherInteractor,
+ new ShadeInteractorLegacyImpl(
mTestScope.getBackgroundScope(),
- deviceProvisioningRepository,
- mDisableFlagsRepository,
- mDozeParameters,
- sceneContainerFlags,
- () -> sceneInteractor,
mKeyguardRepository,
- keyguardTransitionInteractor,
- powerInteractor,
- new FakeUserSetupRepository(),
- mUserSwitcherInteractor,
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
splitShadeStateController),
mShadeRepository
- );
+ )
+ );
+
+ mActiveNotificationsInteractor =
+ new ActiveNotificationsInteractor(new ActiveNotificationListRepository());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -355,13 +363,13 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
- mFeatureFlags,
mInteractionJankMonitor,
mShadeLogger,
mDumpManager,
mock(KeyguardFaceAuthInteractor.class),
mShadeRepository,
mShadeInteractor,
+ mActiveNotificationsInteractor,
new JavaAdapter(mTestScope.getBackgroundScope()),
mCastController,
splitShadeStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 7cb6d931ea8b..997e0e27ef9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -285,6 +285,20 @@ public class QuickSettingsControllerTest extends QuickSettingsControllerBaseTest
}
@Test
+ public void updateQsState_fullscreenTrue() {
+ mQsController.setExpanded(true);
+ mQsController.updateQsState();
+ assertThat(mShadeRepository.getLegacyQsFullscreen().getValue()).isTrue();
+ }
+
+ @Test
+ public void updateQsState_fullscreenFalse() {
+ mQsController.setExpanded(false);
+ mQsController.updateQsState();
+ assertThat(mShadeRepository.getLegacyQsFullscreen().getValue()).isFalse();
+ }
+
+ @Test
public void shadeExpanded_onKeyguard() {
mStatusBarStateController.setState(KEYGUARD);
// set maxQsExpansion in NPVC
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index e920687753fd..5f8777ddcbb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -158,6 +158,15 @@ class ShadeRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun updateLegacyLockscreenShadeTracking() =
+ testScope.runTest {
+ assertThat(underTest.legacyLockscreenShadeTracking.value).isEqualTo(false)
+
+ underTest.setLegacyLockscreenShadeTracking(true)
+ assertThat(underTest.legacyLockscreenShadeTracking.value).isEqualTo(true)
+ }
+
+ @Test
fun updateLegacyQsTracking() =
testScope.runTest {
assertThat(underTest.legacyQsTracking.value).isEqualTo(false)
@@ -198,4 +207,22 @@ class ShadeRepositoryImplTest : SysuiTestCase() {
underTest.setLegacyIsQsExpanded(true)
assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(true)
}
+
+ @Test
+ fun updateLegacyExpandImmediate() =
+ testScope.runTest {
+ assertThat(underTest.legacyExpandImmediate.value).isEqualTo(false)
+
+ underTest.setLegacyExpandImmediate(true)
+ assertThat(underTest.legacyExpandImmediate.value).isEqualTo(true)
+ }
+
+ @Test
+ fun updateLegacyQsFullscreen() =
+ testScope.runTest {
+ assertThat(underTest.legacyQsFullscreen.value).isEqualTo(false)
+
+ underTest.setLegacyQsFullscreen(true)
+ assertThat(underTest.legacyQsFullscreen.value).isEqualTo(true)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index ff7443f10bf3..61e4370949ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.shade.data.repository
+package com.android.systemui.shade.domain.interactor
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
@@ -22,13 +22,11 @@ import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -45,10 +43,10 @@ import com.android.systemui.power.data.repository.FakePowerRepository
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.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
@@ -62,14 +60,12 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
@SmallTest
-class ShadeInteractorTest : SysuiTestCase() {
+class ShadeInteractorImplTest : SysuiTestCase() {
@SysUISingleton
@Component(
@@ -79,7 +75,7 @@ class ShadeInteractorTest : SysuiTestCase() {
UserDomainLayerModule::class,
]
)
- interface TestComponent : SysUITestComponent<ShadeInteractor> {
+ interface TestComponent : SysUITestComponent<ShadeInteractorImpl> {
val configurationRepository: FakeConfigurationRepository
val deviceProvisioningRepository: FakeDeviceProvisioningRepository
@@ -105,7 +101,7 @@ class ShadeInteractorTest : SysuiTestCase() {
private val dozeParameters: DozeParameters = mock()
private val testComponent: TestComponent =
- DaggerShadeInteractorTest_TestComponent.factory()
+ DaggerShadeInteractorImplTest_TestComponent.factory()
.create(
test = this,
featureFlags =
@@ -446,154 +442,6 @@ class ShadeInteractorTest : SysuiTestCase() {
}
@Test
- fun lockscreenShadeExpansion_idle_onScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.Shade
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is idle on the scene
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
- }
-
- @Test
- fun lockscreenShadeExpansion_idle_onDifferentScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is idle on a different scene
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_toScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = key,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN expansion matches the progress
- assertThat(expansionAmount).isEqualTo(.4f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_fromScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = key,
- toScene = SceneKey.Lockscreen,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN expansion reflects the progress
- assertThat(expansionAmount).isEqualTo(.6f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is starting to between different scenes
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.Shade,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition state is partially complete
- progress.value = .4f
-
- // THEN expansion is still 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is still 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
fun userInteractingWithShade_shadeDraggedUpAndDown() =
testComponent.runTest() {
val actual by collectLastValue(underTest.isUserInteractingWithShade)
@@ -815,199 +663,6 @@ class ShadeInteractorTest : SysuiTestCase() {
// THEN user is not interacting
assertThat(actual).isFalse()
}
- @Test
- fun userInteracting_idle() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.Shade
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is idle
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
- }
-
- @Test
- fun userInteracting_transitioning_toScene_programmatic() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = key,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
- }
-
- @Test
- fun userInteracting_transitioning_toScene_userInputDriven() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = key,
- progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
- }
-
- @Test
- fun userInteracting_transitioning_fromScene_programmatic() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = key,
- toScene = SceneKey.Lockscreen,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
- }
-
- @Test
- fun userInteracting_transitioning_fromScene_userInputDriven() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is starting to move to the scene
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = key,
- toScene = SceneKey.Lockscreen,
- progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN interacting is true
- assertThat(interacting).isTrue()
- }
-
- @Test
- fun userInteracting_transitioning_toAndFromDifferentScenes() =
- testComponent.runTest() {
- // GIVEN an interacting flow based on transitions to and from a scene
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
- val interacting by collectLastValue(interactingFlow)
-
- // WHEN transition state is starting to between different scenes
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.QuickSettings,
- progress = MutableStateFlow(0f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
- }
@Test
fun isShadeTouchable_isFalse_whenFrpIsActive() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
new file mode 100644
index 000000000000..92eb6ed52c14
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -0,0 +1,415 @@
+/*
+ * 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.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class ShadeInteractorLegacyImplTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ShadeInteractorLegacyImpl> {
+
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val powerRepository: FakePowerRepository
+ val sceneInteractor: SceneInteractor
+ val shadeRepository: FakeShadeRepository
+ val userRepository: FakeUserRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerShadeInteractorLegacyImplTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+
+ @Before
+ fun setUp() {
+ runBlocking {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or
+ UserInfo.FLAG_ADMIN or
+ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ )
+ testComponent.apply {
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ }
+ }
+ }
+
+ @Test
+ fun fullShadeExpansionWhenShadeLocked() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+ assertThat(actual).isEqualTo(0.5f)
+
+ shadeRepository.setLockscreenShadeExpansion(0.8f)
+ assertThat(actual).isEqualTo(0.8f)
+ }
+
+ @Test
+ fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+ shadeRepository.setQsExpansion(.5f)
+ shadeRepository.setLegacyShadeExpansion(.7f)
+ runCurrent()
+
+ // THEN legacy shade expansion is passed through
+ assertThat(actual).isEqualTo(.7f)
+ }
+
+ @Test
+ fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeRepository.setQsExpansion(.5f)
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN shade expansion is zero
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyShadeExpansion(.6f)
+
+ // THEN shade expansion is zero
+ assertThat(actual).isEqualTo(.6f)
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeDraggedUpAndDown() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged halfway and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(.6f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade completes expansion stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadePartiallyExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade partially expanded
+ shadeRepository.setLegacyShadeExpansion(.4f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN tracking is stopped
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade goes back to collapsed
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeCollapsed() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade expanded and not tracking input
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged up halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithQs_qsDraggedUpAndDown() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithQs)
+ // GIVEN qs collapsed and not tracking input
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN qs tracking starts
+ shadeRepository.setLegacyQsTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged down halfway
+ shadeRepository.setQsExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully expanded but tracking is not stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully collapsed but tracking is not stopped
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged halfway and tracking is stopped
+ shadeRepository.setQsExpansion(.6f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs completes expansion stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
new file mode 100644
index 000000000000..729f3f840e1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -0,0 +1,583 @@
+/*
+ * 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.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+@SmallTest
+class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ShadeInteractorSceneContainerImpl> {
+
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val powerRepository: FakePowerRepository
+ val sceneInteractor: SceneInteractor
+ val shadeRepository: FakeShadeRepository
+ val userRepository: FakeUserRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerShadeInteractorSceneContainerImplTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+
+ @Before
+ fun setUp() {
+ runBlocking {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or
+ UserInfo.FLAG_ADMIN or
+ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ )
+ testComponent.apply {
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ }
+ }
+ }
+
+ @Ignore("b/309825977")
+ @Test
+ fun qsExpansionWhenInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.qsExpansion)
+
+ // WHEN split shade is enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+ val progress = MutableStateFlow(.3f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN legacy shade expansion is passed through
+ Truth.assertThat(actual).isEqualTo(.3f)
+ }
+
+ @Ignore("b/309825977")
+ @Test
+ fun qsExpansionWhenNotInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.qsExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ val progress = MutableStateFlow(.3f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN shade expansion is zero
+ Truth.assertThat(actual).isEqualTo(.7f)
+ }
+
+ @Test
+ fun qsFullscreen_falseWhenTransitioning() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isQsFullscreen)
+
+ // WHEN scene transition active
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ val progress = MutableStateFlow(.3f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN QS is not fullscreen
+ Truth.assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun qsFullscreen_falseWhenIdleNotQS() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isQsFullscreen)
+
+ // WHEN Idle but not on QuickSettings scene
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.Shade)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN QS is not fullscreen
+ Truth.assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun qsFullscreen_trueWhenIdleQS() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isQsFullscreen)
+
+ // WHEN Idle on QuickSettings scene
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.QuickSettings)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN QS is fullscreen
+ Truth.assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on the scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onDifferentScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on a different scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion matches the progress
+ Truth.assertThat(expansionAmount).isEqualTo(.4f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_fromScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion reflects the progress
+ Truth.assertThat(expansionAmount).isEqualTo(.6f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially complete
+ progress.value = .4f
+
+ // THEN expansion is still 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is still 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun userInteracting_idle() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is idle
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_programmatic() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_userInputDriven() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_programmatic() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_userInputDriven() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toAndFromDifferentScenes() =
+ testComponent.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.QuickSettings,
+ progress = MutableStateFlow(0f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 589f9aeba19f..e2640af136a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -19,10 +19,11 @@ package com.android.systemui.shade.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -76,6 +77,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
scope = testScope.backgroundScope,
)
+ private val qsFlexiglassAdapter = FakeQSSceneAdapter { _, _ -> mock() }
+
private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
private lateinit var underTest: ShadeSceneViewModel
@@ -96,13 +99,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- bouncerInteractor =
- utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
shadeHeaderViewModel = shadeHeaderViewModel,
+ qsSceneAdapter = qsFlexiglassAdapter,
+ notifications = utils.notificationsPlaceholderViewModel(),
)
}
@@ -130,7 +129,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
@@ -142,7 +141,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index b8fe2f911b50..cb83e7c7adbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -20,10 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.smartspace.config.BcSmartspaceConfigProvider
-import com.android.systemui.util.mockito.whenever
-import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -45,16 +42,7 @@ class BcSmartspaceConfigProviderTest : SysuiTestCase() {
}
@Test
- fun isDefaultDateWeatherDisabled_flagIsTrue_returnsTrue() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
+ fun isDefaultDateWeatherDisabled_returnsTrue() {
assertTrue(configProvider.isDefaultDateWeatherDisabled)
}
-
- @Test
- fun isDefaultDateWeatherDisabled_flagIsFalse_returnsFalse() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- assertFalse(configProvider.isDefaultDateWeatherDisabled)
- }
}
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 d925d0acd8d8..ffde6015c127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -23,9 +23,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
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.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
@@ -48,16 +50,20 @@ class DragDownHelperTest : SysuiTestCase() {
private val dragDownloadCallback: LockscreenShadeTransitionController = mock()
private val expandableView: ExpandableView = mock()
private val expandCallback: ExpandHelper.Callback = mock()
+ private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver = mock()
@Before
fun setUp() {
whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+ whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
dragDownHelper = DragDownHelper(
falsingManager,
falsingCollector,
dragDownloadCallback,
- mContext
+ naturalScrollingSettingObserver,
+ FakeShadeRepository(),
+ mContext,
).also {
it.expandCallback = expandCallback
}
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 970a0f7a3605..3efcf7b7c26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,8 +5,8 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
+import com.android.systemui.TestMocksModule
import com.android.systemui.ExpandHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
@@ -14,6 +14,7 @@ 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.plugins.qs.QS
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -99,6 +100,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
+ @Mock lateinit var naturalScrollingSettingObserver: NaturalScrollingSettingObserver
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -123,6 +125,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
+ whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
testComponent =
DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory()
@@ -185,6 +188,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
shadeInteractor = testComponent.shadeInteractor,
powerInteractor = testComponent.powerInteractor,
splitShadeStateController = ResourcesSplitShadeStateController(),
+ naturalScrollingSettingObserver = naturalScrollingSettingObserver,
)
transitionController.addCallback(transitionControllerCallback)
@@ -607,9 +611,9 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Component.Factory
interface Factory {
fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
): TestComponent
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
index 3a9c24a7109c..01fe40f6dff6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
@@ -49,6 +49,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -120,6 +121,7 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa
private UserInfo mCurrentUser;
private UserInfo mSecondaryUser;
private UserInfo mWorkUser;
+ private UserInfo mCommunalUser;
private FakeSettings mSettings;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
private NotificationEntry mCurrentUserNotif;
@@ -142,12 +144,18 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa
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));
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
@@ -178,6 +186,26 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa
}
@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);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 43adc69be13f..ae3214267ff5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -64,6 +64,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -136,6 +137,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
private UserInfo mCurrentUser;
private UserInfo mSecondaryUser;
private UserInfo mWorkUser;
+ private UserInfo mCommunalUser;
private FakeSettings mSettings;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
private NotificationEntry mCurrentUserNotif;
@@ -158,14 +160,20 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
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.getUsers()).thenReturn(Lists.newArrayList(
- mCurrentUser, mWorkUser, mSecondaryUser));
+ mCurrentUser, mWorkUser, mSecondaryUser, mCommunalUser));
when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
mSecondaryUser));
+ when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
+ Lists.newArrayList(mSecondaryUser, mCommunalUser));
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
@@ -211,6 +219,26 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
}
@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);
@@ -295,6 +323,20 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
}
@Test
+ public void testCurrentUserPrivateNotificationsNullChannel() {
+ // GIVEN current user allows private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+ .setChannel(null)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ // THEN the notification is not 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,
@@ -713,7 +755,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_AVAILABLE);
- assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ int numProfiles = android.multiuser.Flags.supportCommunalProfile() ? 3 : 2;
+ assertEquals(numProfiles, mLockscreenUserManager.mCurrentProfiles.size());
}
@Test
@@ -723,7 +766,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_UNAVAILABLE);
- assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ int numProfiles = android.multiuser.Flags.supportCommunalProfile() ? 3 : 2;
+ assertEquals(numProfiles, mLockscreenUserManager.mCurrentProfiles.size());
}
@Test
@@ -735,7 +779,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
assertEquals(0, mLockscreenUserManager.mCurrentManagedProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ int numProfiles = android.multiuser.Flags.supportCommunalProfile() ? 3 : 2;
+ assertEquals(numProfiles, mLockscreenUserManager.mCurrentProfiles.size());
assertEquals(1, mLockscreenUserManager.mCurrentManagedProfiles.size());
}
@@ -748,7 +793,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
assertEquals(0, mLockscreenUserManager.mCurrentManagedProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ int numProfiles = android.multiuser.Flags.supportCommunalProfile() ? 3 : 2;
+ assertEquals(numProfiles, mLockscreenUserManager.mCurrentProfiles.size());
assertEquals(1, mLockscreenUserManager.mCurrentManagedProfiles.size());
}
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 4b79a499bf90..8fa7cd291851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -44,6 +44,8 @@ import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
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.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
@@ -153,23 +155,25 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
mock(),
mock(),
powerInteractor)
- shadeInteractor = ShadeInteractor(
+ shadeInteractor = ShadeInteractorImpl(
testScope.backgroundScope,
FakeDeviceProvisioningRepository(),
FakeDisableFlagsRepository(),
mock(),
- sceneContainerFlags,
- utils::sceneInteractor,
keyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
FakeUserSetupRepository(),
mock(),
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()),
- shadeRepository,
+ ShadeInteractorLegacyImpl(
+ testScope.backgroundScope,
+ keyguardRepository,
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ ResourcesSplitShadeStateController()),
+ shadeRepository,
+ )
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 9036f22a792a..8440e00a89f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -38,7 +38,6 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
@@ -205,10 +204,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
- // tests.
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
`when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
@@ -260,17 +255,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
deviceProvisionedListener = deviceProvisionedCaptor.value
}
- @Test(expected = RuntimeException::class)
- fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
- // GIVEN the feature flag is disabled
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- // WHEN we try to build the view
- controller.buildAndConnectWeatherView(fakeParent)
-
- // THEN an exception is thrown
- }
-
@Test
fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
// GIVEN an unprovisioned device and an attempt to connect
@@ -332,6 +316,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
clearInvocations(plugin)
// WHEN the session is closed
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -376,20 +362,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
configChangeListener.onThemeChanged()
// We update the new text color to match the wallpaper color
- verify(smartspaceView).setPrimaryTextColor(anyInt())
- }
-
- @Test
- fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the theme changes
- configChangeListener.onThemeChanged()
-
- // We update the new text color to match the wallpaper color
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setPrimaryTextColor(anyInt())
@@ -404,20 +376,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
// We pass that along to the view
- verify(smartspaceView).setDozeAmount(0.7f)
- }
-
- @Test
- fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the doze amount changes
- statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
-
- // We pass that along to the view
verify(dateSmartspaceView).setDozeAmount(0.7f)
verify(weatherSmartspaceView).setDozeAmount(0.7f)
verify(smartspaceView).setDozeAmount(0.7f)
@@ -472,7 +430,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testAllTargetsAreFilteredExceptWeatherWhenNotificationsAreDisabled() {
+ fun testAllTargetsAreFilteredInclWeatherWhenNotificationsAreDisabled() {
// GIVEN the active user doesn't allow any notifications on lockscreen
setShowNotifications(userHandlePrimary, false)
connectSession()
@@ -488,7 +446,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
sessionListener.onTargetsAvailable(targets)
// THEN all non-sensitive content is still shown
- verify(plugin).onTargetsAvailable(eq(listOf(targets[3])))
+ verify(plugin).onTargetsAvailable(emptyList())
}
@Test
@@ -519,8 +477,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherTargetIsFilteredOut() {
connectSession()
// WHEN we receive a list of targets
@@ -670,8 +627,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherDataUpdates() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
@@ -699,33 +655,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testSessionListener_ifDecouplingDisabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
- connectSession()
-
- clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
- // WHEN we receive a list of targets
- val targets = listOf(
- makeWeatherTargetWithExtras(
- id = 1,
- userHandle = userHandlePrimary,
- description = "Sunny",
- state = WeatherData.WeatherStateIcon.SUNNY.id,
- temperature = "32",
- useCelsius = false),
- makeTarget(2, userHandlePrimary, isSensitive = true)
- )
-
- sessionListener.onTargetsAvailable(targets)
-
- verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
- w.description == "Sunny" &&
- w.state == WeatherData.WeatherStateIcon.SUNNY &&
- w.temperature == 32 && !w.useCelsius
- })
- }
-
- @Test
fun testSettingsAreReloaded() {
// GIVEN a connected session where the privacy settings later flip to false
connectSession()
@@ -781,6 +710,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
connectSession()
// WHEN we are told to cleanup
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -816,16 +747,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testWeatherViewUsesSameSession() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
- // GIVEN a connected session
- connectSession()
-
- // No checks is needed here, since connectSession() already checks internally that
- // createSmartspaceSession is invoked only once.
- }
-
- @Test
fun testViewGetInitializedWithBypassEnabledState() {
// GIVEN keyguard bypass is enabled.
`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -853,31 +774,29 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
private fun connectSession() {
- if (controller.isDateWeatherDecoupled()) {
- val dateView = controller.buildAndConnectDateView(fakeParent)
- dateSmartspaceView = dateView as SmartspaceView
- fakeParent.addView(dateView)
- controller.stateChangeListener.onViewAttachedToWindow(dateView)
-
- verify(dateSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(dateSmartspaceView).registerDataProvider(datePlugin)
-
- verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
- verify(dateSmartspaceView).setDozeAmount(0.5f)
-
- val weatherView = controller.buildAndConnectWeatherView(fakeParent)
- weatherSmartspaceView = weatherView as SmartspaceView
- fakeParent.addView(weatherView)
- controller.stateChangeListener.onViewAttachedToWindow(weatherView)
-
- verify(weatherSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
-
- verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
- verify(weatherSmartspaceView).setDozeAmount(0.5f)
- }
+ val dateView = controller.buildAndConnectDateView(fakeParent)
+ dateSmartspaceView = dateView as SmartspaceView
+ fakeParent.addView(dateView)
+ controller.stateChangeListener.onViewAttachedToWindow(dateView)
+
+ verify(dateSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(dateSmartspaceView).registerDataProvider(datePlugin)
+
+ verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(dateSmartspaceView).setDozeAmount(0.5f)
+
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent)
+ weatherSmartspaceView = weatherView as SmartspaceView
+ fakeParent.addView(weatherView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+
+ verify(weatherSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
+
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(weatherSmartspaceView).setDozeAmount(0.5f)
val view = controller.buildAndConnectView(fakeParent)
smartspaceView = view as SmartspaceView
@@ -918,10 +837,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(smartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setDozeAmount(0.5f)
- if (controller.isDateWeatherDecoupled()) {
- clearInvocations(dateSmartspaceView)
- clearInvocations(weatherSmartspaceView)
- }
+ clearInvocations(dateSmartspaceView)
+ clearInvocations(weatherSmartspaceView)
clearInvocations(smartspaceView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index 428574bb15f8..fa5fad06b671 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfte
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
@@ -57,6 +58,7 @@ class StackCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var groupExpansionManagerImpl: GroupExpansionManagerImpl
@Mock private lateinit var notificationIconAreaController: NotificationIconAreaController
@Mock private lateinit var renderListInteractor: RenderNotificationListInteractor
+ @Mock private lateinit var activeNotificationsInteractor: ActiveNotificationsInteractor
@Mock private lateinit var stackController: NotifStackController
@Mock private lateinit var section: NotifSection
@@ -75,6 +77,7 @@ class StackCoordinatorTest : SysuiTestCase() {
groupExpansionManagerImpl,
notificationIconAreaController,
renderListInteractor,
+ activeNotificationsInteractor,
)
coordinator.attach(pipeline)
afterRenderListListener = withArgCaptor {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index d47993793fc0..f3094cdd4faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.data.repository
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
new file mode 100644
index 000000000000..4ab3cd49b297
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import org.junit.Test
+
+@SmallTest
+class ActiveNotificationsInteractorTest : SysuiTestCase() {
+
+ @Component(modules = [SysUITestModule::class])
+ @SysUISingleton
+ interface TestComponent : SysUITestComponent<ActiveNotificationsInteractor> {
+ val activeNotificationListRepository: ActiveNotificationListRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(@BindsInstance test: SysuiTestCase): TestComponent
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerActiveNotificationsInteractorTest_TestComponent.factory().create(test = this)
+
+ @Test
+ fun testAreAnyNotificationsPresent_isTrue() =
+ testComponent.runTest {
+ val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent)
+
+ activeNotificationListRepository.setActiveNotifs(2)
+ runCurrent()
+
+ assertThat(areAnyNotificationsPresent).isTrue()
+ assertThat(underTest.areAnyNotificationsPresentValue).isTrue()
+ }
+
+ @Test
+ fun testAreAnyNotificationsPresent_isFalse() =
+ testComponent.runTest {
+ val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent)
+
+ activeNotificationListRepository.setActiveNotifs(0)
+ runCurrent()
+
+ assertThat(areAnyNotificationsPresent).isFalse()
+ assertThat(underTest.areAnyNotificationsPresentValue).isFalse()
+ }
+
+ @Test
+ fun testHasClearableNotifications_whenHasClearableAlertingNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isTrue()
+ }
+
+ @Test
+ fun testHasClearableNotifications_whenHasClearableSilentNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isTrue()
+ }
+
+ @Test
+ fun testHasClearableNotifications_whenHasNoClearableNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index 707026e42009..b7750795fe71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.domain.interactor
import android.app.StatusBarManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index bb6f1b6a2850..bb3113a72e92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -14,20 +14,17 @@
package com.android.systemui.statusbar.notification.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index a64ac674a91c..22c5bae93489 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -114,9 +114,46 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
+ public void testSetClearAllButtonText_resourceOnlyFetchedOnce() {
+ int resId = R.string.clear_all_notifications_text;
+ mView.setClearAllButtonText(resId);
+ verify(mSpyContext).getString(eq(resId));
+
+ clearInvocations(mSpyContext);
+
+ assertThat(((TextView) mView.findViewById(R.id.dismiss_text))
+ .getText().toString()).contains("Clear all");
+
+ // Set it a few more times, it shouldn't lead to the resource being fetched again
+ mView.setClearAllButtonText(resId);
+ mView.setClearAllButtonText(resId);
+
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
+ public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() {
+ int resId = R.string.accessibility_clear_all;
+ mView.setClearAllButtonDescription(resId);
+ verify(mSpyContext).getString(eq(resId));
+
+ clearInvocations(mSpyContext);
+
+ assertThat(((TextView) mView.findViewById(R.id.dismiss_text))
+ .getContentDescription().toString()).contains("Clear all notifications");
+
+ // Set it a few more times, it shouldn't lead to the resource being fetched again
+ mView.setClearAllButtonDescription(resId);
+ mView.setClearAllButtonDescription(resId);
+
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
public void testSetMessageString_resourceOnlyFetchedOnce() {
- mView.setMessageString(R.string.unlock_to_see_notif_text);
- verify(mSpyContext).getString(eq(R.string.unlock_to_see_notif_text));
+ int resId = R.string.unlock_to_see_notif_text;
+ mView.setMessageString(resId);
+ verify(mSpyContext).getString(eq(resId));
clearInvocations(mSpyContext);
@@ -124,22 +161,23 @@ public class FooterViewTest extends SysuiTestCase {
.getText().toString()).contains("Unlock");
// Set it a few more times, it shouldn't lead to the resource being fetched again
- mView.setMessageString(R.string.unlock_to_see_notif_text);
- mView.setMessageString(R.string.unlock_to_see_notif_text);
+ mView.setMessageString(resId);
+ mView.setMessageString(resId);
verify(mSpyContext, never()).getString(anyInt());
}
@Test
public void testSetMessageIcon_resourceOnlyFetchedOnce() {
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
- verify(mSpyContext).getDrawable(eq(R.drawable.ic_friction_lock_closed));
+ int resId = R.drawable.ic_friction_lock_closed;
+ mView.setMessageIcon(resId);
+ verify(mSpyContext).getDrawable(eq(resId));
clearInvocations(mSpyContext);
// Set it a few more times, it shouldn't lead to the resource being fetched again
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
+ mView.setMessageIcon(resId);
+ mView.setMessageIcon(resId);
verify(mSpyContext, never()).getDrawable(anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 57a7c3c7e2bf..94dcf7a18514 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -18,37 +18,222 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Optional
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class FooterViewModelTest : SysuiTestCase() {
- private val repository = ActiveNotificationListRepository()
- private val interactor = SeenNotificationsInteractor(repository)
- private val underTest = FooterViewModel(interactor)
+ private lateinit var footerViewModel: FooterViewModel
- @Test
- fun testMessageVisible_whenFilteredNotifications() = runTest {
- val message by collectLastValue(underTest.message)
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ActivatableNotificationViewModelModule::class,
+ FooterViewModelModule::class,
+ HeadlessSystemUserModeModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<Optional<FooterViewModel>> {
+ val activeNotificationListRepository: ActiveNotificationListRepository
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val shadeRepository: FakeShadeRepository
+ val powerRepository: FakePowerRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
- repository.hasFilteredOutSeenNotifications.value = true
+ private val testComponent: TestComponent =
+ DaggerFooterViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ )
+ )
- assertThat(message?.visible).isTrue()
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
+
+ // The underTest in the component is Optional, because that matches the provider we
+ // currently have for the footer view model.
+ footerViewModel = testComponent.underTest.get()
}
@Test
- fun testMessageVisible_whenNoFilteredNotifications() = runTest {
- val message by collectLastValue(underTest.message)
+ fun testMessageVisible_whenFilteredNotifications() =
+ testComponent.runTest {
+ val message by collectLastValue(footerViewModel.message)
- repository.hasFilteredOutSeenNotifications.value = false
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
- assertThat(message?.visible).isFalse()
- }
+ assertThat(message?.visible).isTrue()
+ }
+
+ @Test
+ fun testMessageVisible_whenNoFilteredNotifications() =
+ testComponent.runTest {
+ val message by collectLastValue(footerViewModel.message)
+
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+
+ assertThat(message?.visible).isFalse()
+ }
+
+ @Test
+ fun testClearAllButtonVisible_whenHasClearableNotifs() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ assertThat(button?.isVisible?.value).isTrue()
+ }
+
+ @Test
+ fun testClearAllButtonVisible_whenHasNoClearableNotifs() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(button?.isVisible?.value).isFalse()
+ }
+
+ @Test
+ fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+ runCurrent()
+
+ // WHEN shade is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setLegacyShadeExpansion(1f)
+ // AND QS not expanded
+ shadeRepository.setQsExpansion(0f)
+ // AND device is awake
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // AND there are clearable notifications
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ // THEN button visibility should animate
+ assertThat(button?.isVisible?.isAnimating).isTrue()
+ }
+
+ @Test
+ fun testClearAllButtonAnimating_whenShadeNotExpanded() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+ runCurrent()
+
+ // WHEN shade is collapsed
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setLegacyShadeExpansion(0f)
+ // AND QS not expanded
+ shadeRepository.setQsExpansion(0f)
+ // AND device is awake
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // AND there are clearable notifications
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ // THEN button visibility should not animate
+ assertThat(button?.isVisible?.isAnimating).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 05deb1cc75c7..360a373711d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -17,19 +17,19 @@ package com.android.systemui.statusbar.notification.icon.domain.interactor
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
-import com.android.systemui.statusbar.notification.shared.activeNotificationModel
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPulsing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index c2c33de015ef..10d4c627bc4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -18,14 +18,13 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -39,6 +38,8 @@ import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
@@ -94,7 +95,6 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
FakeFeatureFlagsClassicModule {
setDefault(Flags.FACE_AUTH_REFACTOR)
set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
- setDefault(Flags.NEW_AOD_TRANSITION)
},
mocks =
TestMocksModule(
@@ -115,6 +115,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
lastSleepReason = WakeSleepReason.OTHER,
)
}
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 1a04a3ea291a..e264fc07489a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -20,14 +20,12 @@ import android.graphics.Rect
import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -42,11 +40,13 @@ import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationIconViewStateRepository
-import com.android.systemui.statusbar.notification.shared.activeNotificationModel
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
import com.android.systemui.statusbar.phone.data.repository.FakeDarkIconRepository
@@ -389,4 +389,31 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() {
assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
assertThat(isolatedIcon?.isAnimating).isFalse()
}
+
+ @Test
+ fun isolatedIcon_updateWhenIconDataChanges() =
+ testComponent.runTest {
+ val icon: Icon = mock()
+ val isolatedIcon by collectLastValue(underTest.isolatedIcon)
+ runCurrent()
+
+ headsUpViewStateRepository.isolatedNotification.value = "notif1"
+ runCurrent()
+
+ activeNotificationsRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ groupKey = "group",
+ statusBarIcon = icon
+ )
+ )
+ }
+ .build()
+ runCurrent()
+
+ assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 1c621613c403..0a9bac91004a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -76,6 +76,7 @@ import com.android.systemui.statusbar.policy.BatteryController;
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.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -126,6 +127,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
DeviceProvisionedController mDeviceProvisionedController;
FakeSystemClock mSystemClock;
FakeGlobalSettings mGlobalSettings;
+ FakeEventLog mEventLog;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -138,6 +140,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
mSystemClock = new FakeSystemClock();
mGlobalSettings = new FakeGlobalSettings();
mGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
+ mEventLog = new FakeEventLog();
mNotifInterruptionStateProvider =
new NotificationInterruptStateProviderImpl(
@@ -155,7 +158,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
mUserTracker,
mDeviceProvisionedController,
mSystemClock,
- mGlobalSettings);
+ mGlobalSettings,
+ mEventLog);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
@@ -995,11 +999,25 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
}
+ @Test
+ public void shouldNotBubbleUp_suspended() {
+ assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createSuspendedBubble()))
+ .isFalse();
+ }
+
+ private NotificationEntry createSuspendedBubble() {
+ return createBubble(null, null, true);
+ }
+
private NotificationEntry createBubble() {
- return createBubble(null, null);
+ return createBubble(null, null, false);
}
private NotificationEntry createBubble(String groupKey, Integer groupAlert) {
+ return createBubble(groupKey, groupAlert, false);
+ }
+
+ private NotificationEntry createBubble(String groupKey, Integer groupAlert, Boolean suspended) {
Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0,
new Intent().setPackage(mContext.getPackageName()),
@@ -1027,6 +1045,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
.setNotification(n)
.setImportance(IMPORTANCE_HIGH)
.setCanBubble(true)
+ .setSuspended(suspended)
.build();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 1d2055ec2e30..1c7fd565c289 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.interruption
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
@@ -35,6 +36,10 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
+ init {
+ setFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR)
+ }
+
override val provider by lazy {
NotificationInterruptStateProviderWrapper(
NotificationInterruptStateProviderImpl(
@@ -44,7 +49,7 @@ class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecision
statusBarStateController,
keyguardStateController,
headsUpManager,
- logger,
+ oldLogger,
mainHandler,
flags,
keyguardNotificationVisibilityProvider,
@@ -53,6 +58,7 @@ class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecision
deviceProvisionedController,
systemClock,
globalSettings,
+ eventLog
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index 80d941a40cb5..df6f0d716577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.interruption
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
@@ -28,18 +29,26 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() {
+ init {
+ setFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR)
+ }
+
override val provider by lazy {
VisualInterruptionDecisionProviderImpl(
ambientDisplayConfiguration,
batteryController,
+ deviceProvisionedController,
+ eventLog,
globalSettings,
headsUpManager,
keyguardNotificationVisibilityProvider,
- logger,
+ keyguardStateController,
+ newLogger,
mainHandler,
powerManager,
statusBarStateController,
systemClock,
+ uiEventLogger,
userTracker,
)
}
@@ -50,6 +59,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -59,6 +69,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -68,6 +79,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -77,6 +89,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -86,6 +99,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -95,6 +109,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -104,6 +119,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -113,6 +129,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -193,6 +210,10 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
assertShouldBubble(buildBubbleEntry())
}
+ private fun assertFsiNotSuppressed() {
+ forEachFsiState { assertShouldFsi(buildFsiEntry()) }
+ }
+
private fun withCondition(condition: VisualInterruptionCondition, block: () -> Unit) {
provider.addCondition(condition)
block()
@@ -208,14 +229,14 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro
private class TestCondition(
types: Set<VisualInterruptionType>,
val onShouldSuppress: () -> Boolean
- ) : VisualInterruptionCondition(types = types, reason = "") {
+ ) : VisualInterruptionCondition(types = types, reason = "test condition") {
override fun shouldSuppress(): Boolean = onShouldSuppress()
}
private class TestFilter(
types: Set<VisualInterruptionType>,
val onShouldSuppress: (NotificationEntry) -> Boolean = { true }
- ) : VisualInterruptionFilter(types = types, reason = "") {
+ ) : VisualInterruptionFilter(types = types, reason = "test filter") {
override fun shouldSuppress(entry: NotificationEntry) = onShouldSuppress(entry)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 7f12b22f2b4e..7babff5e0b2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -20,6 +20,9 @@ import android.app.ActivityManager
import android.app.Notification
import android.app.Notification.BubbleMetadata
import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.FLAG_FOREGROUND_SERVICE
+import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED
+import android.app.Notification.FLAG_USER_INITIATED_JOB
import android.app.Notification.GROUP_ALERT_ALL
import android.app.Notification.GROUP_ALERT_CHILDREN
import android.app.Notification.GROUP_ALERT_SUMMARY
@@ -29,6 +32,7 @@ import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT
+import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK
import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
import android.app.PendingIntent
@@ -40,11 +44,16 @@ import android.graphics.drawable.Icon
import android.hardware.display.FakeAmbientDisplayConfiguration
import android.os.Looper
import android.os.PowerManager
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
import android.provider.Settings.Global.HEADS_UP_ON
+import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.FakeStatusBarStateController
@@ -56,36 +65,64 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN
+import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.FakeEventLog
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.leaks.FakeBatteryController
+import com.android.systemui.utils.leaks.FakeKeyguardStateController
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.android.systemui.utils.os.FakeHandler
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assert.assertEquals
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.`when` as whenever
abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ private val fakeLogBuffer =
+ LogBuffer(
+ name = "FakeLog",
+ maxSize = 1,
+ logcatEchoTracker =
+ object : LogcatEchoTracker {
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean =
+ true
+
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
+ },
+ systrace = false
+ )
+
private val leakCheck = LeakCheckedTest.SysuiLeakCheck()
protected val ambientDisplayConfiguration = FakeAmbientDisplayConfiguration(context)
protected val batteryController = FakeBatteryController(leakCheck)
- protected val deviceProvisionedController: DeviceProvisionedController = mock()
+ protected val deviceProvisionedController = FakeDeviceProvisionedController()
+ protected val eventLog = FakeEventLog()
protected val flags: NotifPipelineFlags = mock()
- protected val globalSettings = FakeGlobalSettings()
+ protected val globalSettings =
+ FakeGlobalSettings().also { it.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON) }
protected val headsUpManager: HeadsUpManager = mock()
protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
mock()
- protected val keyguardStateController: KeyguardStateController = mock()
- protected val logger: NotificationInterruptLogger = mock()
+ protected val keyguardStateController = FakeKeyguardStateController(leakCheck)
protected val mainHandler = FakeHandler(Looper.getMainLooper())
+ protected val newLogger = VisualInterruptionDecisionLogger(fakeLogBuffer)
+ protected val oldLogger = NotificationInterruptLogger(fakeLogBuffer)
protected val powerManager: PowerManager = mock()
protected val statusBarStateController = FakeStatusBarStateController()
protected val systemClock = FakeSystemClock()
@@ -113,14 +150,9 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
@Before
fun setUp() {
- globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON)
-
val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
userTracker.set(listOf(user), /* currentUserIndex = */ 0)
- whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
- .thenReturn(false)
-
provider.start()
}
@@ -128,66 +160,86 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
fun testShouldPeek() {
ensurePeekState()
assertShouldHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPeek_settingDisabled() {
ensurePeekState { hunSettingEnabled = false }
assertShouldNotHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotPeek_packageSnoozed() {
+ fun testShouldNotPeek_packageSnoozed_withoutFsi() {
ensurePeekState { hunSnoozed = true }
assertShouldNotHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
- fun testShouldPeek_packageSnoozedButFsi() {
- ensurePeekState { hunSnoozed = true }
- assertShouldHeadsUp(buildFsiEntry())
+ fun testShouldPeek_packageSnoozed_withFsi() {
+ val entry = buildFsiEntry()
+ forEachPeekableFsiState {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldHeadsUp(entry)
+
+ // The old code logs a UiEvent when a HUN snooze is bypassed because the notification
+ // has an FSI, but that doesn't fit into the new code's suppressor-based logic, so we're
+ // not reimplementing it.
+ if (provider !is NotificationInterruptStateProviderWrapper) {
+ assertNoEventsLogged()
+ }
+ }
}
@Test
fun testShouldNotPeek_alreadyBubbled() {
ensurePeekState { statusBarState = SHADE }
assertShouldNotHeadsUp(buildPeekEntry { isBubble = true })
+ assertNoEventsLogged()
}
@Test
fun testShouldPeek_isBubble_shadeLocked() {
ensurePeekState { statusBarState = SHADE_LOCKED }
assertShouldHeadsUp(buildPeekEntry { isBubble = true })
+ assertNoEventsLogged()
}
@Test
fun testShouldPeek_isBubble_keyguard() {
ensurePeekState { statusBarState = KEYGUARD }
assertShouldHeadsUp(buildPeekEntry { isBubble = true })
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPeek_dnd() {
ensurePeekState()
assertShouldNotHeadsUp(buildPeekEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK })
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPeek_notImportant() {
ensurePeekState()
assertShouldNotHeadsUp(buildPeekEntry { importance = IMPORTANCE_DEFAULT })
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPeek_screenOff() {
ensurePeekState { isScreenOn = false }
assertShouldNotHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPeek_dreaming() {
ensurePeekState { isDreaming = true }
assertShouldNotHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
@@ -197,95 +249,142 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
}
@Test
- fun testShouldPeek_notQuiteOldEnoughWhen() {
+ fun testLogsHunOldWhen() {
+ assertNoEventsLogged()
+
+ ensurePeekState()
+ val entry = buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) }
+
+ // The old code logs the "old when" UiEvent unconditionally, so don't expect that it hasn't.
+ if (provider !is NotificationInterruptStateProviderWrapper) {
+ provider.makeUnloggedHeadsUpDecision(entry)
+ assertNoEventsLogged()
+ }
+
+ provider.makeAndLogHeadsUpDecision(entry)
+ assertUiEventLogged(HUN_SUPPRESSED_OLD_WHEN, entry.sbn.uid, entry.sbn.packageName)
+ assertNoSystemEventLogged()
+ }
+
+ @Test
+ fun testShouldPeek_oldWhen_now() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(0) })
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldPeek_oldWhen_notOldEnough() {
ensurePeekState()
assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS - 1) })
+ assertNoEventsLogged()
}
@Test
- fun testShouldPeek_zeroWhen() {
+ fun testShouldPeek_oldWhen_zeroWhen() {
ensurePeekState()
assertShouldHeadsUp(buildPeekEntry { whenMs = 0L })
+ assertNoEventsLogged()
}
@Test
- fun testShouldPeek_oldWhenButFsi() {
+ fun testShouldPeek_oldWhen_negativeWhen() {
ensurePeekState()
- assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) })
+ assertShouldHeadsUp(buildPeekEntry { whenMs = -1L })
+ assertNoEventsLogged()
}
@Test
- fun testShouldPeek_defaultLegacySuppressor() {
+ fun testShouldPeek_oldWhen_fullScreenIntent() {
ensurePeekState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldHeadsUp(buildPeekEntry())
+ assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) })
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotPeek_legacySuppressInterruptions() {
+ fun testShouldPeek_oldWhen_foregroundService() {
ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotHeadsUp(buildPeekEntry())
+ assertShouldHeadsUp(
+ buildPeekEntry {
+ whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS)
+ isForegroundService = true
+ }
+ )
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotPeek_legacySuppressAwakeInterruptions() {
+ fun testShouldPeek_oldWhen_userInitiatedJob() {
ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldNotHeadsUp(buildPeekEntry())
+ assertShouldHeadsUp(
+ buildPeekEntry {
+ whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS)
+ isUserInitiatedJob = true
+ }
+ )
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotPeek_legacySuppressAwakeHeadsUp() {
- ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+ fun testShouldNotPeek_hiddenOnKeyguard() {
+ ensurePeekState({ keyguardShouldHideNotification = true })
assertShouldNotHeadsUp(buildPeekEntry())
+ assertNoEventsLogged()
}
@Test
- fun testShouldPulse() {
- ensurePulseState()
- assertShouldHeadsUp(buildPulseEntry())
+ fun testShouldPeek_defaultLegacySuppressor() {
+ ensurePeekState()
+ withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPeekEntry()) }
+ assertNoEventsLogged()
}
@Test
- fun testShouldPulse_defaultLegacySuppressor() {
- ensurePulseState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldHeadsUp(buildPulseEntry())
+ fun testShouldNotPeek_legacySuppressInterruptions() {
+ ensurePeekState()
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotPulse_legacySuppressInterruptions() {
- ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotHeadsUp(buildPulseEntry())
+ fun testShouldNotPeek_legacySuppressAwakeInterruptions() {
+ ensurePeekState()
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+ assertNoEventsLogged()
}
@Test
- fun testShouldPulse_legacySuppressAwakeInterruptions() {
- ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldHeadsUp(buildPulseEntry())
+ fun testShouldNotPeek_legacySuppressAwakeHeadsUp() {
+ ensurePeekState()
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+ assertNoEventsLogged()
}
@Test
- fun testShouldPulse_legacySuppressAwakeHeadsUp() {
+ fun testShouldPulse() {
ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
assertShouldHeadsUp(buildPulseEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPulse_disabled() {
ensurePulseState { pulseOnNotificationsEnabled = false }
assertShouldNotHeadsUp(buildPulseEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPulse_batterySaver() {
ensurePulseState { isAodPowerSave = true }
assertShouldNotHeadsUp(buildPulseEntry())
+ assertNoEventsLogged()
}
@Test
@@ -294,204 +393,486 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
assertShouldNotHeadsUp(
buildPulseEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_AMBIENT }
)
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPulse_visibilityOverridePrivate() {
ensurePulseState()
assertShouldNotHeadsUp(buildPulseEntry { visibilityOverride = VISIBILITY_PRIVATE })
+ assertNoEventsLogged()
}
@Test
fun testShouldNotPulse_importanceLow() {
ensurePulseState()
assertShouldNotHeadsUp(buildPulseEntry { importance = IMPORTANCE_LOW })
+ assertNoEventsLogged()
}
- private fun withPeekAndPulseEntry(
- extendEntry: EntryBuilder.() -> Unit,
- block: (NotificationEntry) -> Unit
- ) {
- ensurePeekState()
- block(buildPeekEntry(extendEntry))
+ @Test
+ fun testShouldNotPulse_hiddenOnKeyguard() {
+ ensurePulseState({ keyguardShouldHideNotification = true })
+ assertShouldNotHeadsUp(buildPulseEntry())
+ assertNoEventsLogged()
+ }
+ @Test
+ fun testShouldPulse_defaultLegacySuppressor() {
ensurePulseState()
- block(buildPulseEntry(extendEntry))
+ withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPulseEntry()) }
+ assertNoEventsLogged()
}
@Test
- fun testShouldHeadsUp_groupedSummaryNotif_groupAlertAll() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_ALL
- }) {
- assertShouldHeadsUp(it)
+ fun testShouldNotPulse_legacySuppressInterruptions() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotHeadsUp(buildPulseEntry())
}
+ assertNoEventsLogged()
}
@Test
- fun testShouldHeadsUp_groupedSummaryNotif_groupAlertSummary() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_SUMMARY
- }) {
- assertShouldHeadsUp(it)
+ fun testShouldPulse_legacySuppressAwakeInterruptions() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldHeadsUp(buildPulseEntry())
}
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotHeadsUp_groupedSummaryNotif_groupAlertChildren() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_CHILDREN
- }) {
- assertShouldNotHeadsUp(it)
+ fun testShouldPulse_legacySuppressAwakeHeadsUp() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldHeadsUp(buildPulseEntry())
}
+ assertNoEventsLogged()
}
- @Test
- fun testShouldHeadsUp_ungroupedSummaryNotif_groupAlertChildren() {
- withPeekAndPulseEntry({
- isGrouped = false
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_CHILDREN
- }) {
- assertShouldHeadsUp(it)
- }
+ private fun withPeekAndPulseEntry(
+ extendEntry: EntryBuilder.() -> Unit,
+ block: (NotificationEntry) -> Unit
+ ) {
+ ensurePeekState()
+ block(buildPeekEntry(extendEntry))
+
+ ensurePulseState()
+ block(buildPulseEntry(extendEntry))
}
@Test
- fun testShouldHeadsUp_groupedChildNotif_groupAlertAll() {
+ fun testShouldNotHeadsUp_suppressiveGroupAlertBehavior() {
withPeekAndPulseEntry({
isGrouped = true
isGroupSummary = false
- groupAlertBehavior = GROUP_ALERT_ALL
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
}) {
- assertShouldHeadsUp(it)
+ assertShouldNotHeadsUp(it)
+ assertNoEventsLogged()
}
}
@Test
- fun testShouldHeadsUp_groupedChildNotif_groupAlertChildren() {
+ fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notSuppressive() {
withPeekAndPulseEntry({
isGrouped = true
isGroupSummary = false
groupAlertBehavior = GROUP_ALERT_CHILDREN
}) {
assertShouldHeadsUp(it)
+ assertNoEventsLogged()
}
}
@Test
- fun testShouldNotHeadsUp_groupedChildNotif_groupAlertSummary() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = false
- groupAlertBehavior = GROUP_ALERT_SUMMARY
- }) {
- assertShouldNotHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldHeadsUp_ungroupedChildNotif_groupAlertSummary() {
+ fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notGrouped() {
withPeekAndPulseEntry({
isGrouped = false
isGroupSummary = false
groupAlertBehavior = GROUP_ALERT_SUMMARY
}) {
assertShouldHeadsUp(it)
+ assertNoEventsLogged()
}
}
@Test
fun testShouldNotHeadsUp_justLaunchedFsi() {
- withPeekAndPulseEntry({ hasJustLaunchedFsi = true }) { assertShouldNotHeadsUp(it) }
+ withPeekAndPulseEntry({ hasJustLaunchedFsi = true }) {
+ assertShouldNotHeadsUp(it)
+ assertNoEventsLogged()
+ }
}
@Test
fun testShouldBubble_withIntentAndIcon() {
ensureBubbleState()
assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = false })
+ assertNoEventsLogged()
}
@Test
fun testShouldBubble_withShortcut() {
ensureBubbleState()
assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = true })
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotBubble_notAllowed() {
+ fun testShouldBubble_suppressiveGroupAlertBehavior() {
ensureBubbleState()
- assertShouldNotBubble(buildBubbleEntry { canBubble = false })
+ assertShouldBubble(
+ buildBubbleEntry {
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
+ }
+ )
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotBubble_noBubbleMetadata() {
+ fun testShouldNotBubble_notABubble() {
+ ensureBubbleState()
+ assertShouldNotBubble(
+ buildBubbleEntry {
+ isBubble = false
+ hasBubbleMetadata = false
+ }
+ )
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotBubble_missingBubbleMetadata() {
ensureBubbleState()
assertShouldNotBubble(buildBubbleEntry { hasBubbleMetadata = false })
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotBubble_notAllowedToBubble() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { canBubble = false })
+ assertNoEventsLogged()
}
@Test
fun testShouldBubble_defaultLegacySuppressor() {
ensureBubbleState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldBubble(buildBubbleEntry())
+ withLegacySuppressor(neverSuppresses) { assertShouldBubble(buildBubbleEntry()) }
+ assertNoEventsLogged()
}
@Test
fun testShouldNotBubble_legacySuppressInterruptions() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+ assertNoEventsLogged()
}
@Test
fun testShouldNotBubble_legacySuppressAwakeInterruptions() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldNotBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+ assertNoEventsLogged()
}
@Test
fun testShouldBubble_legacySuppressAwakeHeadsUp() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
- assertShouldBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldBubble(buildBubbleEntry())
+ }
+ assertNoEventsLogged()
}
@Test
- fun testShouldNotAlert_hiddenOnKeyguard() {
- ensurePeekState({ keyguardShouldHideNotification = true })
- assertShouldNotHeadsUp(buildPeekEntry())
-
- ensurePulseState({ keyguardShouldHideNotification = true })
- assertShouldNotHeadsUp(buildPulseEntry())
-
+ fun testShouldNotBubble_hiddenOnKeyguard() {
ensureBubbleState({ keyguardShouldHideNotification = true })
assertShouldNotBubble(buildBubbleEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotBubble_bubbleAppSuspended() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { packageSuspended = true })
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotFsi_noFullScreenIntent() {
+ forEachFsiState {
+ assertShouldNotFsi(buildFsiEntry { hasFsi = false })
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_showStickyHun() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ hasFsi = false
+ isStickyAndNotDemoted = true
+ }
+ )
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_onlyDnd() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT },
+ expectWouldInterruptWithoutDnd = true
+ )
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_notImportantEnough() {
+ forEachFsiState {
+ assertShouldNotFsi(buildFsiEntry { importance = IMPORTANCE_DEFAULT })
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_notOnlyDnd() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ importance = IMPORTANCE_DEFAULT
+ },
+ expectWouldInterruptWithoutDnd = false
+ )
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_suppressiveGroupAlertBehavior() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testLogsFsiSuppressiveGroupAlertBehavior() {
+ ensureNotInteractiveFsiState()
+ val entry = buildFsiEntry {
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }
+
+ val decision = provider.makeUnloggedFullScreenIntentDecision(entry)
+ assertNoEventsLogged()
+
+ provider.logFullScreenIntentDecision(decision)
+ assertUiEventLogged(
+ FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
+ entry.sbn.uid,
+ entry.sbn.packageName
+ )
+ assertSystemEventLogged("231322873", entry.sbn.uid, "groupAlertBehavior")
+ }
+
+ @Test
+ fun testShouldFsi_suppressiveGroupAlertBehavior_notGrouped() {
+ forEachFsiState {
+ assertShouldFsi(
+ buildFsiEntry {
+ isGrouped = false
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }
+ )
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressiveGroupAlertBehavior_notSuppressive() {
+ forEachFsiState {
+ assertShouldFsi(
+ buildFsiEntry {
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_ALL
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_suppressiveBubbleMetadata() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ hasBubbleMetadata = true
+ bubbleSuppressesNotification = true
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testLogsFsiSuppressiveBubbleMetadata() {
+ ensureNotInteractiveFsiState()
+ val entry = buildFsiEntry {
+ hasBubbleMetadata = true
+ bubbleSuppressesNotification = true
+ }
+
+ val decision = provider.makeUnloggedFullScreenIntentDecision(entry)
+ assertNoEventsLogged()
+
+ provider.logFullScreenIntentDecision(decision)
+ assertUiEventLogged(
+ FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA,
+ entry.sbn.uid,
+ entry.sbn.packageName
+ )
+ assertSystemEventLogged("274759612", entry.sbn.uid, "bubbleMetadata")
+ }
+
+ @Test
+ fun testShouldNotFsi_packageSuspended() {
+ forEachFsiState {
+ assertShouldNotFsi(buildFsiEntry { packageSuspended = true })
+ assertNoEventsLogged()
+ }
}
@Test
fun testShouldFsi_notInteractive() {
ensureNotInteractiveFsiState()
assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldFsi_dreaming() {
ensureDreamingFsiState()
assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
}
@Test
fun testShouldFsi_keyguard() {
ensureKeyguardFsiState()
assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotFsi_expectedToHun() {
+ forEachPeekableFsiState {
+ ensurePeekState()
+ assertShouldNotFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_expectedToHun_hunSnoozed() {
+ forEachPeekableFsiState {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldNotFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldFsi_lockedShade() {
+ ensureLockedShadeFsiState()
+ assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldFsi_keyguardOccluded() {
+ ensureKeyguardOccludedFsiState()
+ assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldFsi_deviceNotProvisioned() {
+ ensureDeviceNotProvisionedFsiState()
+ assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
+ fun testShouldNotFsi_noHunOrKeyguard() {
+ ensureNoHunOrKeyguardFsiState()
+ assertShouldNotFsi(buildFsiEntry())
+ }
+
+ @Test
+ fun testLogsFsiNoHunOrKeyguard() {
+ ensureNoHunOrKeyguardFsiState()
+ val entry = buildFsiEntry()
+
+ val decision = provider.makeUnloggedFullScreenIntentDecision(entry)
+ assertNoEventsLogged()
+
+ provider.logFullScreenIntentDecision(decision)
+ assertUiEventLogged(FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD, entry.sbn.uid, entry.sbn.packageName)
+ assertSystemEventLogged("231322873", entry.sbn.uid, "no hun or keyguard")
+ }
+
+ @Test
+ fun testShouldFsi_defaultLegacySuppressor() {
+ forEachFsiState {
+ withLegacySuppressor(neverSuppresses) { assertShouldFsi(buildFsiEntry()) }
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressInterruptions() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesInterruptions) { assertShouldFsi(buildFsiEntry()) }
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressAwakeInterruptions() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldFsi(buildFsiEntry())
+ }
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressAwakeHeadsUp() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { assertShouldFsi(buildFsiEntry()) }
+ assertNoEventsLogged()
+ }
}
protected data class State(
@@ -505,6 +886,9 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
var keyguardShouldHideNotification: Boolean? = null,
var pulseOnNotificationsEnabled: Boolean? = null,
var statusBarState: Int? = null,
+ var keyguardIsShowing: Boolean = false,
+ var keyguardIsOccluded: Boolean = false,
+ var deviceProvisioned: Boolean = true
)
protected fun setState(state: State): Unit =
@@ -536,6 +920,11 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
}
statusBarState?.let { statusBarStateController.state = it }
+
+ keyguardStateController.isOccluded = keyguardIsOccluded
+ keyguardStateController.isShowing = keyguardIsShowing
+
+ deviceProvisionedController.deviceProvisioned = deviceProvisioned
}
protected fun ensureState(block: State.() -> Unit) =
@@ -565,33 +954,111 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
protected fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block)
protected fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState {
- isDreaming = false
isInteractive = false
- statusBarState = SHADE
run(block)
}
protected fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
isDreaming = true
+ run(block)
+ }
+
+ protected fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
isInteractive = true
+ isDreaming = false
+ statusBarState = KEYGUARD
+ run(block)
+ }
+
+ protected fun ensureLockedShadeFsiState(block: State.() -> Unit = {}) = ensureState {
+ // It is assumed *but not checked in the code* that statusBarState is SHADE_LOCKED.
+ isInteractive = true
+ isDreaming = false
statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = true
+ keyguardIsOccluded = false
run(block)
}
- protected fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
+ protected fun ensureKeyguardOccludedFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = true
+ keyguardIsOccluded = true
+ run(block)
+ }
+
+ protected fun ensureDeviceNotProvisionedFsiState(block: State.() -> Unit = {}) = ensureState {
isInteractive = true
- statusBarState = KEYGUARD
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = false
+ deviceProvisioned = false
run(block)
}
+ protected fun ensureNoHunOrKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = false
+ deviceProvisioned = true
+ run(block)
+ }
+
+ protected fun forEachFsiState(block: () -> Unit) {
+ ensureNotInteractiveFsiState()
+ block()
+
+ ensureDreamingFsiState()
+ block()
+
+ ensureKeyguardFsiState()
+ block()
+
+ ensureLockedShadeFsiState()
+ block()
+
+ ensureKeyguardOccludedFsiState()
+ block()
+
+ ensureDeviceNotProvisionedFsiState()
+ block()
+ }
+
+ private fun forEachPeekableFsiState(extendState: State.() -> Unit = {}, block: () -> Unit) {
+ ensureLockedShadeFsiState(extendState)
+ block()
+
+ ensureKeyguardOccludedFsiState(extendState)
+ block()
+
+ ensureDeviceNotProvisionedFsiState(extendState)
+ block()
+ }
+
+ protected fun withLegacySuppressor(
+ suppressor: NotificationInterruptSuppressor,
+ block: () -> Unit
+ ) {
+ provider.addLegacySuppressor(suppressor)
+ block()
+ provider.removeLegacySuppressor(suppressor)
+ }
+
protected fun assertShouldHeadsUp(entry: NotificationEntry) =
- provider.makeUnloggedHeadsUpDecision(entry).let {
+ provider.makeAndLogHeadsUpDecision(entry).let {
assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt)
}
protected fun assertShouldNotHeadsUp(entry: NotificationEntry) =
- provider.makeUnloggedHeadsUpDecision(entry).let {
+ provider.makeAndLogHeadsUpDecision(entry).let {
assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt)
}
@@ -607,30 +1074,57 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
protected fun assertShouldFsi(entry: NotificationEntry) =
provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ provider.logFullScreenIntentDecision(it)
assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt)
}
- protected fun assertShouldNotFsi(entry: NotificationEntry) =
+ protected fun assertShouldNotFsi(
+ entry: NotificationEntry,
+ expectWouldInterruptWithoutDnd: Boolean? = null
+ ) =
provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ provider.logFullScreenIntentDecision(it)
assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt)
+ if (expectWouldInterruptWithoutDnd != null) {
+ assertEquals(
+ "unexpected wouldInterruptWithoutDnd for FSI: ${it.logReason}",
+ expectWouldInterruptWithoutDnd,
+ it.wouldInterruptWithoutDnd
+ )
+ }
}
protected class EntryBuilder(val context: Context) {
- var importance = IMPORTANCE_DEFAULT
- var suppressedVisualEffects: Int? = null
- var whenMs: Long? = null
- var visibilityOverride: Int? = null
- var hasFsi = false
- var canBubble: Boolean? = null
- var isBubble = false
- var hasBubbleMetadata = false
+ // Set on BubbleMetadata:
var bubbleIsShortcut = false
- var bubbleSuppressesNotification: Boolean? = null
+ var bubbleSuppressesNotification = false
+
+ // Set on Notification.Builder:
+ var whenMs: Long? = null
var isGrouped = false
- var isGroupSummary: Boolean? = null
+ var isGroupSummary = false
var groupAlertBehavior: Int? = null
+ var hasBubbleMetadata = false
+ var hasFsi = false
+
+ // Set on Notification:
+ var isForegroundService = false
+ var isUserInitiatedJob = false
+ var isBubble = false
+ var isStickyAndNotDemoted = false
+
+ // Set on NotificationEntryBuilder:
+ var importance = IMPORTANCE_DEFAULT
+ var canBubble: Boolean? = null
+
+ // Set on NotificationEntry:
var hasJustLaunchedFsi = false
+ // Set on ModifiedRankingBuilder:
+ var packageSuspended = false
+ var visibilityOverride: Int? = null
+ var suppressedVisualEffects: Int? = null
+
private fun buildBubbleMetadata(): BubbleMetadata {
val builder =
if (bubbleIsShortcut) {
@@ -647,62 +1141,87 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
)
}
- bubbleSuppressesNotification?.let { builder.setSuppressNotification(it) }
+ if (bubbleSuppressesNotification) {
+ builder.setSuppressNotification(true)
+ }
return builder.build()
}
fun build() =
Notification.Builder(context, TEST_CHANNEL_ID)
- .apply {
- setContentTitle(TEST_CONTENT_TITLE)
- setContentText(TEST_CONTENT_TEXT)
+ .also { nb ->
+ nb.setContentTitle(TEST_CONTENT_TITLE)
+ nb.setContentText(TEST_CONTENT_TEXT)
- if (hasFsi) {
- setFullScreenIntent(mock(), /* highPriority = */ true)
+ whenMs?.let { nb.setWhen(it) }
+
+ if (isGrouped) {
+ nb.setGroup(TEST_GROUP_KEY)
}
- whenMs?.let { setWhen(it) }
+ if (isGroupSummary) {
+ nb.setGroupSummary(true)
+ }
+
+ groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) }
if (hasBubbleMetadata) {
- setBubbleMetadata(buildBubbleMetadata())
+ nb.setBubbleMetadata(buildBubbleMetadata())
}
- if (isGrouped) {
- setGroup(TEST_GROUP_KEY)
+ if (hasFsi) {
+ nb.setFullScreenIntent(mock(), /* highPriority = */ true)
}
-
- isGroupSummary?.let { setGroupSummary(it) }
-
- groupAlertBehavior?.let { setGroupAlertBehavior(it) }
}
.build()
- .apply {
+ .also { n ->
+ if (isForegroundService) {
+ n.flags = n.flags or FLAG_FOREGROUND_SERVICE
+ }
+
+ if (isUserInitiatedJob) {
+ n.flags = n.flags or FLAG_USER_INITIATED_JOB
+ }
+
if (isBubble) {
- flags = flags or FLAG_BUBBLE
+ n.flags = n.flags or FLAG_BUBBLE
+ }
+
+ if (isStickyAndNotDemoted) {
+ n.flags = n.flags or FLAG_FSI_REQUESTED_BUT_DENIED
}
}
.let { NotificationEntryBuilder().setNotification(it) }
- .apply {
- setPkg(TEST_PACKAGE)
- setOpPkg(TEST_PACKAGE)
- setTag(TEST_TAG)
-
- setImportance(importance)
- setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
+ .also { neb ->
+ neb.setPkg(TEST_PACKAGE)
+ neb.setOpPkg(TEST_PACKAGE)
+ neb.setTag(TEST_TAG)
+
+ neb.setImportance(importance)
+ neb.setChannel(
+ NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
+ )
- canBubble?.let { setCanBubble(it) }
+ canBubble?.let { neb.setCanBubble(it) }
}
.build()!!
- .also {
+ .also { ne ->
if (hasJustLaunchedFsi) {
- it.notifyFullScreenIntentLaunched()
+ ne.notifyFullScreenIntentLaunched()
}
- modifyRanking(it)
- .apply {
- suppressedVisualEffects?.let { setSuppressedVisualEffects(it) }
- visibilityOverride?.let { setVisibilityOverride(it) }
+ if (isStickyAndNotDemoted) {
+ assertFalse(ne.isDemoted)
+ }
+
+ modifyRanking(ne)
+ .also { mrb ->
+ if (packageSuspended) {
+ mrb.setSuspended(true)
+ }
+ visibilityOverride?.let { mrb.setVisibilityOverride(it) }
+ suppressedVisualEffects?.let { mrb.setSuppressedVisualEffects(it) }
}
.build()
}
@@ -723,6 +1242,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
}
protected fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ isBubble = true
canBubble = true
hasBubbleMetadata = true
run(block)
@@ -734,6 +1254,45 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
run(block)
}
+ private fun assertNoEventsLogged() {
+ assertNoUiEventLogged()
+ assertNoSystemEventLogged()
+ }
+
+ private fun assertNoUiEventLogged() {
+ assertEquals(0, uiEventLogger.numLogs())
+ }
+
+ private fun assertUiEventLogged(uiEventId: UiEventEnum, uid: Int, packageName: String) {
+ assertEquals(1, uiEventLogger.numLogs())
+
+ val event = uiEventLogger.get(0)
+ assertEquals(uiEventId.id, event.eventId)
+ assertEquals(uid, event.uid)
+ assertEquals(packageName, event.packageName)
+ }
+
+ private fun assertNoSystemEventLogged() {
+ assertEquals(0, eventLog.events.size)
+ }
+
+ private fun assertSystemEventLogged(number: String, uid: Int, description: String) {
+ assertEquals(1, eventLog.events.size)
+
+ val event = eventLog.events[0]
+ assertEquals(0x534e4554, event.tag)
+
+ val value = event.value
+ assertTrue(value is Array<*>)
+
+ if (value is Array<*>) {
+ assertEquals(3, value.size)
+ assertEquals(number, value[0])
+ assertEquals(uid, value[1])
+ assertEquals(description, value[2])
+ }
+ }
+
private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
index ca105f3e52ea..16c5c8a98253 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar.notification.shared
-import android.graphics.drawable.Icon
import com.google.common.truth.Correspondence
val byKey: Correspondence<ActiveNotificationModel, String> =
@@ -38,30 +37,3 @@ val byIsLastMessageFromReply: Correspondence<ActiveNotificationModel, Boolean> =
)
val byIsPulsing: Correspondence<ActiveNotificationModel, Boolean> =
Correspondence.transforming({ it?.isPulsing }, "has an isPulsing value of")
-
-fun activeNotificationModel(
- key: String,
- groupKey: String? = null,
- isAmbient: Boolean = false,
- isRowDismissed: Boolean = false,
- isSilent: Boolean = false,
- isLastMessageFromReply: Boolean = false,
- isSuppressedFromStatusBar: Boolean = false,
- isPulsing: Boolean = false,
- aodIcon: Icon? = null,
- shelfIcon: Icon? = null,
- statusBarIcon: Icon? = null,
-) =
- ActiveNotificationModel(
- key = key,
- groupKey = groupKey,
- isAmbient = isAmbient,
- isRowDismissed = isRowDismissed,
- isSilent = isSilent,
- isLastMessageFromReply = isLastMessageFromReply,
- isSuppressedFromStatusBar = isSuppressedFromStatusBar,
- isPulsing = isPulsing,
- aodIcon = aodIcon,
- shelfIcon = shelfIcon,
- statusBarIcon = statusBarIcon,
- )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 7423c2decaec..917569ca787b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -19,16 +19,16 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
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 590389035e92..ff5c02622e4e 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
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-import android.content.res.Resources;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -53,7 +52,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
@@ -74,7 +72,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
@@ -84,17 +81,15 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -170,8 +165,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
+ private final ActiveNotificationListRepository mActiveNotificationsRepository =
+ new ActiveNotificationListRepository();
+
+ private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
+ new ActiveNotificationsInteractor(mActiveNotificationsRepository);
+
private final SeenNotificationsInteractor mSeenNotificationsInteractor =
- new SeenNotificationsInteractor(new ActiveNotificationListRepository());
+ new SeenNotificationsInteractor(mActiveNotificationsRepository);
private NotificationStackScrollLayoutController mController;
@@ -701,6 +702,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,
+ mActiveNotificationsInteractor,
mSeenNotificationsInteractor,
mViewBinder,
mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6203531cabab..ba5ba2c31a42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.stack;
import static android.view.View.GONE;
import static android.view.WindowInsets.Type.ime;
+import static com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -83,6 +84,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -161,7 +163,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// in the constructor.
mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
- mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION);
mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
// Inject dependencies before initializing the layout
@@ -906,6 +908,20 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
assertEquals(bottomImeInset, mStackScrollerInternal.mBottomInset);
}
+ @Test
+ public void testSetMaxDisplayedNotifications_notifiesListeners() {
+ ExpandableView.OnHeightChangedListener listener =
+ mock(ExpandableView.OnHeightChangedListener.class);
+ Runnable runnable = mock(Runnable.class);
+ mStackScroller.setOnHeightChangedListener(listener);
+ mStackScroller.setOnHeightChangedRunnable(runnable);
+
+ mStackScroller.setMaxDisplayedNotifications(50);
+
+ verify(listener).onHeightChanged(mNotificationShelf, false);
+ verify(runnable).run();
+ }
+
private void setBarStateForTest(int state) {
// Can't inject this through the listener or we end up on the actual implementation
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
new file mode 100644
index 000000000000..ac20683b4f49
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -0,0 +1,302 @@
+/*
+ * 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.stack.domain.interactor
+
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.Surface
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_90
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.domain.interactor.ConfigurationInteractorImpl
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
+import com.android.systemui.power.shared.model.WakefulnessState.STARTING_TO_SLEEP
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.util.animation.data.repository.FakeAnimationStatusRepository
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+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.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+open class HideNotificationsInteractorTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val animationStatus = FakeAnimationStatusRepository()
+ private val configurationController = FakeConfigurationController()
+ private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+ private val powerRepository = FakePowerRepository()
+ private val powerInteractor =
+ PowerInteractor(
+ repository = powerRepository,
+ falsingCollector = mock(),
+ screenOffAnimationController = mock(),
+ statusBarStateController = mock()
+ )
+
+ private val unfoldTransitionRepository =
+ UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+ private val unfoldTransitionInteractor =
+ UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+
+ private val configurationRepository =
+ ConfigurationRepositoryImpl(
+ configurationController,
+ context,
+ testScope.backgroundScope,
+ mock()
+ )
+ private val configurationInteractor = ConfigurationInteractorImpl(configurationRepository)
+
+ private lateinit var configuration: Configuration
+ private lateinit var underTest: HideNotificationsInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ configuration = context.resources.configuration
+
+ val testableResources = context.getOrCreateTestableResources()
+ testableResources.overrideConfiguration(configuration)
+
+ updateDisplay()
+
+ underTest =
+ HideNotificationsInteractor(
+ unfoldTransitionInteractor,
+ configurationInteractor,
+ animationStatus,
+ powerInteractor
+ )
+ }
+
+ @Test
+ fun displaySwitch_hidesNotifications() =
+ testScope.runTest {
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+
+ assertThat(values).containsExactly(true).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_sizeIsTheSame_noChangesToNotifications() =
+ testScope.runTest {
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH)
+ runCurrent()
+
+ assertThat(values).isEmpty()
+ }
+
+ @Test
+ fun displaySwitch_sizeIsTheSameAfterRotation_noChangesToNotifications() =
+ testScope.runTest {
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(
+ width = INITIAL_DISPLAY_HEIGHT,
+ height = INITIAL_DISPLAY_WIDTH,
+ rotation = ROTATION_90
+ )
+ runCurrent()
+
+ assertThat(values).isEmpty()
+ }
+
+ @Test
+ fun displaySwitch_noAnimations_screenTurnedOn_showsNotificationsBack() =
+ testScope.runTest {
+ givenAnimationsEnabled(false)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ powerRepository.setScreenPowerState(SCREEN_ON)
+ runCurrent()
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitchUnfold_animationsEnabled_screenTurnedOn_doesNotShowNotifications() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ powerRepository.setScreenPowerState(SCREEN_ON)
+ runCurrent()
+
+ assertThat(values).containsExactly(true).inOrder()
+ }
+
+ @Test
+ fun displaySwitchFold_animationsEnabled_screenTurnedOn_showsNotifications() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH / 2)
+ runCurrent()
+ powerRepository.setScreenPowerState(SCREEN_ON)
+ runCurrent()
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_noAnimations_screenGoesToSleep_showsNotificationsBack() =
+ testScope.runTest {
+ givenAnimationsEnabled(false)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ powerRepository.updateWakefulness(STARTING_TO_SLEEP)
+ runCurrent()
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_animationsEnabled_screenGoesToSleep_showsNotificationsBack() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ powerRepository.updateWakefulness(STARTING_TO_SLEEP)
+ runCurrent()
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_animationsEnabled_unfoldAnimationNotFinished_notificationsHidden() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+
+ assertThat(values).containsExactly(true).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_animationsEnabled_unfoldAnimationFinishes_showsNotificationsBack() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ unfoldTransitionProgressProvider.onTransitionFinished()
+ runCurrent()
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_noEvents_afterTimeout_showsNotificationsBack() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ advanceTimeBy(Duration.ofMillis(10_000).toMillis())
+
+ assertThat(values).containsExactly(true, false).inOrder()
+ }
+
+ @Test
+ fun displaySwitch_noEvents_beforeTimeout_doesNotShowNotifications() =
+ testScope.runTest {
+ givenAnimationsEnabled(true)
+ val values by collectValues(hideNotificationsFlow)
+
+ runCurrent()
+ updateDisplay(width = INITIAL_DISPLAY_WIDTH * 2)
+ runCurrent()
+ advanceTimeBy(Duration.ofMillis(500).toMillis())
+
+ assertThat(values).containsExactly(true).inOrder()
+ }
+
+ private val hideNotificationsFlow: Flow<Boolean>
+ get() = underTest.shouldHideNotifications
+
+ private fun updateDisplay(
+ width: Int = INITIAL_DISPLAY_WIDTH,
+ height: Int = INITIAL_DISPLAY_HEIGHT,
+ @Surface.Rotation rotation: Int = ROTATION_0
+ ) {
+ configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
+ configuration.windowConfiguration.displayRotation = rotation
+
+ configurationController.onConfigurationChanged(configuration)
+ }
+
+ private fun givenAnimationsEnabled(enabled: Boolean) {
+ animationStatus.onAnimationStatusChanged(enabled)
+ }
+
+ private companion object {
+ private const val INITIAL_DISPLAY_WIDTH = 100
+ private const val INITIAL_DISPLAY_HEIGHT = 200
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
new file mode 100644
index 000000000000..f00abc9c3f63
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+
+import android.app.NotificationManager.Policy
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.common.domain.CommonDomainLayerModule
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+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.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModelModule
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
+import com.android.systemui.unfold.UnfoldTransitionModule
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationListViewModelTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ActivatableNotificationViewModelModule::class,
+ CommonDomainLayerModule::class,
+ FooterViewModelModule::class,
+ HeadlessSystemUserModeModule::class,
+ UnfoldTransitionModule.Bindings::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<NotificationListViewModel> {
+ val activeNotificationListRepository: ActiveNotificationListRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val shadeRepository: FakeShadeRepository
+ val zenModeRepository: FakeZenModeRepository
+ val configurationController: FakeConfigurationController
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerNotificationListViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule()
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
+ }
+
+ @Test
+ fun testIsImportantForAccessibility_falseWhenNoNotifs() =
+ testComponent.runTest {
+ val important by collectLastValue(underTest.isImportantForAccessibility)
+
+ // WHEN on lockscreen
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ // AND has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ testScope.runCurrent()
+
+ // THEN not important
+ assertThat(important).isFalse()
+ }
+
+ @Test
+ fun testIsImportantForAccessibility_trueWhenNotifs() =
+ testComponent.runTest {
+ val important by collectLastValue(underTest.isImportantForAccessibility)
+
+ // WHEN on lockscreen
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ // AND has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ runCurrent()
+
+ // THEN is important
+ assertThat(important).isTrue()
+ }
+
+ @Test
+ fun testIsImportantForAccessibility_trueWhenNotKeyguard() =
+ testComponent.runTest {
+ val important by collectLastValue(underTest.isImportantForAccessibility)
+
+ // WHEN not on lockscreen
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ // AND has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ runCurrent()
+
+ // THEN is still important
+ assertThat(important).isTrue()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_trueWhenNoNotifs() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ runCurrent()
+
+ // THEN should show
+ assertThat(shouldShow).isTrue()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_falseWhenNotifs() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ runCurrent()
+
+ // THEN should not show
+ assertThat(shouldShow).isFalse()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_falseWhenQsExpandedDefault() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ // AND quick settings are expanded
+ shadeRepository.legacyQsFullscreen.value = true
+ runCurrent()
+
+ // THEN should not show
+ assertThat(shouldShow).isFalse()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ // AND quick settings are expanded
+ shadeRepository.setQsExpansion(1f)
+ // AND split shade is enabled
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationController.notifyConfigurationChanged()
+ runCurrent()
+
+ // THEN should show
+ assertThat(shouldShow).isTrue()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_falseWhenTransitioningToAOD() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ // AND transitioning to AOD
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0f,
+ )
+ )
+ runCurrent()
+
+ // THEN should not show
+ assertThat(shouldShow).isFalse()
+ }
+
+ @Test
+ fun testShouldShowEmptyShadeView_falseWhenBouncerShowing() =
+ testComponent.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+
+ // WHEN has no notifs
+ activeNotificationListRepository.setActiveNotifs(count = 0)
+ // AND is on bouncer
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ testScope,
+ )
+ runCurrent()
+
+ // THEN should not show
+ assertThat(shouldShow).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_true() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ zenModeRepository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ zenModeRepository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isTrue()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_false() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ zenModeRepository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ zenModeRepository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testHasFilteredOutSeenNotifications_true() =
+ testComponent.runTest {
+ val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
+ runCurrent()
+
+ assertThat(hasFilteredNotifs).isTrue()
+ }
+
+ @Test
+ fun testHasFilteredOutSeenNotifications_false() =
+ testComponent.runTest {
+ val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+ runCurrent()
+
+ assertThat(hasFilteredNotifs).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 22553dfc4cb1..3d7cff44322b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,13 +19,11 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
@@ -39,14 +37,11 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.user.domain.UserDomainLayerModule
-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
import dagger.BindsInstance
import dagger.Component
@@ -84,25 +79,13 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
}
}
- private val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock()
- private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController =
- mock {
- whenever(view).thenReturn(mock())
- whenever(shelfHeight).thenReturn(0)
- }
-
private val testComponent: TestComponent =
DaggerSharedNotificationContainerViewModelTest_TestComponent.factory()
.create(
test = this,
featureFlags =
FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks =
- TestMocksModule(
- notificationStackSizeCalculator = notificationStackSizeCalculator,
- notificationStackScrollLayoutController =
- notificationStackScrollLayoutController,
- )
+ mocks = TestMocksModule(),
)
@Test
@@ -270,8 +253,9 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// Start on lockscreen
showLockscreen()
- keyguardInteractor.sharedNotificationContainerPosition.value =
+ keyguardInteractor.setSharedNotificationContainerPosition(
SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ )
assertThat(position)
.isEqualTo(SharedNotificationContainerPosition(top = 1f, bottom = 2f))
@@ -290,8 +274,9 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// Start on lockscreen
showLockscreen()
- keyguardInteractor.sharedNotificationContainerPosition.value =
+ keyguardInteractor.setSharedNotificationContainerPosition(
SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ )
runCurrent()
// Top should be overridden to 0f
@@ -336,49 +321,75 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun maxNotificationsOnLockscreen() =
testComponent.runTest {
- whenever(
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- any(),
- any(),
- any(),
- any()
- )
- )
- .thenReturn(10)
+ var notificationCount = 10
+ val maxNotifications by
+ collectLastValue(underTest.getMaxNotifications { notificationCount })
+
+ showLockscreen()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.setSharedNotificationContainerPosition(
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ )
+
+ assertThat(maxNotifications).isEqualTo(10)
+
+ // Also updates when directly requested (as it would from NotificationStackScrollLayout)
+ notificationCount = 25
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ assertThat(maxNotifications).isEqualTo(25)
+ }
- val maxNotifications by collectLastValue(underTest.maxNotifications)
+ @Test
+ fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
+ testComponent.runTest {
+ var notificationCount = 10
+ val maxNotifications by
+ collectLastValue(underTest.getMaxNotifications { notificationCount })
showLockscreen()
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.sharedNotificationContainerPosition.value =
+ keyguardInteractor.setSharedNotificationContainerPosition(
SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ )
assertThat(maxNotifications).isEqualTo(10)
+
+ // Shade expanding... still 10
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+ assertThat(maxNotifications).isEqualTo(10)
+
+ notificationCount = 25
+
+ // When shade is expanding by user interaction
+ shadeRepository.setLegacyLockscreenShadeTracking(true)
+
+ // Should still be 10, since the user is interacting
+ assertThat(maxNotifications).isEqualTo(10)
+
+ shadeRepository.setLegacyLockscreenShadeTracking(false)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+
+ // Stopped tracking, show 25
+ assertThat(maxNotifications).isEqualTo(25)
}
@Test
fun maxNotificationsOnShade() =
testComponent.runTest {
- whenever(
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- any(),
- any(),
- any(),
- any()
- )
- )
- .thenReturn(10)
- val maxNotifications by collectLastValue(underTest.maxNotifications)
+ val maxNotifications by collectLastValue(underTest.getMaxNotifications { 10 })
// Show lockscreen with shade expanded
showLockscreenWithShadeExpanded()
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.sharedNotificationContainerPosition.value =
+ keyguardInteractor.setSharedNotificationContainerPosition(
SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ )
// -1 means No Limit
assertThat(maxNotifications).isEqualTo(-1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1d8a3461e546..84cd518cf85a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
import static com.android.systemui.statusbar.phone.AutoTileManager.DEVICE_CONTROLS;
@@ -135,6 +136,8 @@ public class AutoTileManagerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mSecureSettings = new FakeSettings();
+ mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
+
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd,
new String[] {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 164325a431a5..e61b4f81aaee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.google.common.truth.Truth.assertThat;
@@ -50,7 +49,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -130,14 +128,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Mock
private BiometricUnlockInteractor mBiometricUnlockInteractor;
private final FakeSystemClock mSystemClock = new FakeSystemClock();
- private FakeFeatureFlags mFeatureFlags;
private BiometricUnlockController mBiometricUnlockController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true);
@@ -165,7 +160,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mAuthController, mStatusBarStateController,
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
mSystemClock,
- mFeatureFlags,
mDeviceEntryHapticsInteractor,
() -> mSelectedUserInteractor,
mBiometricUnlockInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index e7dad6a2908f..912c27d854fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -18,8 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -30,7 +28,6 @@ import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.os.PowerManager;
import android.os.UserHandle;
-import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.view.HapticFeedbackConstants;
@@ -42,7 +39,6 @@ import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
@@ -53,7 +49,6 @@ import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -94,14 +89,12 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private DozeServiceHost mDozeServiceHost;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private PowerManager mPowerManager;
- @Mock private VibratorHelper mVibratorHelper;
@Mock private Vibrator mVibrator;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@Mock private UserTracker mUserTracker;
@Mock private QSHost mQSHost;
@Mock private ActivityStarter mActivityStarter;
- private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -131,15 +124,13 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
mNotificationStackScrollLayoutController,
mStatusBarHideIconsForBouncerManager,
mPowerManager,
- mVibratorHelper,
Optional.of(mVibrator),
new DisableFlagsLogger(),
DEFAULT_DISPLAY,
mCameraLauncherLazy,
mUserTracker,
mQSHost,
- mActivityStarter,
- mFeatureFlags);
+ mActivityStarter);
when(mUserTracker.getUserHandle()).thenReturn(
UserHandle.of(ActivityManager.getCurrentUser()));
@@ -192,18 +183,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
}
@Test
- public void vibrateOnNavigationKeyDown_oneWayHapticsDisabled_usesVibrate() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
-
- mSbcqCallbacks.vibrateOnNavigationKeyDown();
-
- verify(mVibratorHelper).vibrate(VibrationEffect.EFFECT_TICK);
- }
-
- @Test
- public void vibrateOnNavigationKeyDown_oneWayHapticsEnabled_usesPerformHapticFeedback() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-
+ public void vibrateOnNavigationKeyDown_usesPerformHapticFeedback() {
mSbcqCallbacks.vibrateOnNavigationKeyDown();
verify(mShadeViewController).performHapticFeedback(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 86a5c52bd983..4b1c7e8faa38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -16,23 +16,18 @@
package com.android.systemui.statusbar.phone;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
+import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -48,8 +43,6 @@ import static java.util.Collections.emptySet;
import android.app.ActivityManager;
import android.app.IWallpaperManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.WallpaperManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
@@ -121,6 +114,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
@@ -155,13 +150,13 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -176,6 +171,8 @@ import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.util.EventLog;
+import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
@@ -214,7 +211,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private CentralSurfacesImpl mCentralSurfaces;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
- private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
+ private VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@@ -322,6 +319,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
+ private final FakeEventLog mFakeEventLog = new FakeEventLog();
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
private final FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -329,6 +327,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private final DumpManager mDumpManager = new DumpManager();
private final ScreenLifecycle mScreenLifecycle = new ScreenLifecycle(mDumpManager);
+ private final SceneContainerFlags mSceneContainerFlags = new FakeSceneContainerFlags();
+
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -342,11 +342,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
// For the Shade to animate during the Back gesture, we must enable the animation flag.
mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
- mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
+ mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -354,23 +354,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mFakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
- mNotificationInterruptStateProvider =
- new TestableNotificationInterruptStateProviderImpl(
- mPowerManager,
- mAmbientDisplayConfiguration,
- mStatusBarStateController,
- mKeyguardStateController,
- mBatteryController,
- mHeadsUpManager,
- mock(NotificationInterruptLogger.class),
- new Handler(TestableLooper.get(this).getLooper()),
- mock(NotifPipelineFlags.class),
- mock(KeyguardNotificationVisibilityProvider.class),
- mock(UiEventLogger.class),
- mUserTracker,
- mDeviceProvisionedController,
- mFakeSystemClock,
- mFakeGlobalSettings);
+ mVisualInterruptionDecisionProvider =
+ new NotificationInterruptStateProviderWrapper(
+ new TestableNotificationInterruptStateProviderImpl(
+ mPowerManager,
+ mAmbientDisplayConfiguration,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ mBatteryController,
+ mHeadsUpManager,
+ mock(NotificationInterruptLogger.class),
+ new Handler(TestableLooper.get(this).getLooper()),
+ mock(NotifPipelineFlags.class),
+ mock(KeyguardNotificationVisibilityProvider.class),
+ mock(UiEventLogger.class),
+ mUserTracker,
+ mDeviceProvisionedController,
+ mFakeSystemClock,
+ mFakeGlobalSettings,
+ mFakeEventLog));
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -473,7 +475,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
new FalsingCollectorFake(),
mBroadcastDispatcher,
mNotificationGutsManager,
- mNotificationInterruptStateProvider,
+ mVisualInterruptionDecisionProvider,
new ShadeExpansionStateManager(),
mKeyguardViewMediator,
new DisplayMetrics(),
@@ -555,7 +557,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mAlternateBouncerInteractor,
mUserTracker,
() -> mFingerprintManager,
- mActivityStarter
+ mActivityStarter,
+ mSceneContainerFlags
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
@@ -683,92 +686,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
- when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mStatusBarStateController.isDreaming()).thenReturn(false);
-
- Notification n = new Notification.Builder(getContext(), "a")
- .setGroup("a")
- .setGroupSummary(true)
- .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
- }
-
- @Test
- public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
- when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mStatusBarStateController.isDreaming()).thenReturn(false);
-
- Notification n = new Notification.Builder(getContext(), "a")
- .setGroup("a")
- .setGroupSummary(true)
- .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
- }
-
- @Test
- public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
- when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mStatusBarStateController.isDreaming()).thenReturn(false);
-
- Notification n = new Notification.Builder(getContext(), "a").build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setChannel(new NotificationChannel("id", null, IMPORTANCE_HIGH))
- .setNotification(n)
- .setImportance(IMPORTANCE_HIGH)
- .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
- .build();
-
- assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
- }
-
- @Test
- public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
- when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mStatusBarStateController.isDreaming()).thenReturn(false);
-
- Notification n = new Notification.Builder(getContext(), "a").build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
- }
-
- @Test
public void testDump_DoesNotCrash() {
mCentralSurfaces.dump(new PrintWriter(new ByteArrayOutputStream()), null);
}
@@ -1182,7 +1099,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
UserTracker userTracker,
DeviceProvisionedController deviceProvisionedController,
SystemClock systemClock,
- GlobalSettings globalSettings) {
+ GlobalSettings globalSettings,
+ EventLog eventLog) {
super(
powerManager,
ambientDisplayConfiguration,
@@ -1198,7 +1116,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
userTracker,
deviceProvisionedController,
systemClock,
- globalSettings
+ globalSettings,
+ eventLog
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 0a68406882d4..f71114d92aa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -16,7 +16,14 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -26,6 +33,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -81,4 +89,39 @@ public class LightBarTransitionsControllerTest extends SysuiTestCase {
verify(mApplier).applyDarkIntensity(eq(0f));
}
+ @Test
+ public void gestureNav_noForceNavButtons_expectNotSupportsIconTint() {
+ GestureNavigationSettingsObserver observer = mock(GestureNavigationSettingsObserver.class);
+ doReturn(false).when(observer).areNavigationButtonForcedVisible();
+ mLightBarTransitionsController.setNavigationSettingsObserver(observer);
+ assertFalse(mLightBarTransitionsController.supportsIconTintForNavMode(
+ NAV_BAR_MODE_GESTURAL));
+ }
+
+ @Test
+ public void gestureNav_forceNavButtons_expectSupportsIconTint() {
+ GestureNavigationSettingsObserver observer = mock(GestureNavigationSettingsObserver.class);
+ doReturn(true).when(observer).areNavigationButtonForcedVisible();
+ mLightBarTransitionsController.setNavigationSettingsObserver(observer);
+ assertTrue(mLightBarTransitionsController.supportsIconTintForNavMode(
+ NAV_BAR_MODE_GESTURAL));
+ }
+
+ @Test
+ public void buttonNav_noForceNavButtons_expectNotSupportsIconTint() {
+ GestureNavigationSettingsObserver observer = mock(GestureNavigationSettingsObserver.class);
+ doReturn(false).when(observer).areNavigationButtonForcedVisible();
+ mLightBarTransitionsController.setNavigationSettingsObserver(observer);
+ assertTrue(mLightBarTransitionsController.supportsIconTintForNavMode(
+ NAV_BAR_MODE_3BUTTON));
+ }
+
+ @Test
+ public void buttonNav_forceNavButtons_expectSupportsIconTint() {
+ GestureNavigationSettingsObserver observer = mock(GestureNavigationSettingsObserver.class);
+ doReturn(true).when(observer).areNavigationButtonForcedVisible();
+ mLightBarTransitionsController.setNavigationSettingsObserver(observer);
+ assertTrue(mLightBarTransitionsController.supportsIconTintForNavMode(
+ NAV_BAR_MODE_3BUTTON));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 15c09b53938f..4827c92ce452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1185,14 +1185,11 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void testScrimFocus() {
- mScrimController.transitionTo(ScrimState.AOD);
- assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
- assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable());
-
- mScrimController.transitionTo(ScrimState.KEYGUARD);
- Assert.assertTrue("Should be focusable on keyguard", mScrimBehind.isFocusable());
- Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
+ public void testScrimsAreNotFocusable() {
+ assertFalse("Behind scrim should not be focusable", mScrimBehind.isFocusable());
+ assertFalse("Front scrim should not be focusable", mScrimInFront.isFocusable());
+ assertFalse("Notifications scrim should not be focusable",
+ mNotificationsScrim.isFocusable());
}
@Test
@@ -1263,14 +1260,6 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void testViewsDontHaveFocusHighlight() {
- assertFalse("Scrim shouldn't have focus highlight",
- mScrimInFront.getDefaultFocusHighlightEnabled());
- assertFalse("Scrim shouldn't have focus highlight",
- mScrimBehind.getDefaultFocusHighlightEnabled());
- }
-
- @Test
public void testIsLowPowerMode() {
HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
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 46b3996c4337..225ddb6110c2 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
@@ -178,7 +178,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mNotificationShadeWindowController.getWindowRootView())
.thenReturn(mNotificationShadeWindowView);
@@ -771,7 +771,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.addCallback(mCallback);
// GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// THEN the touch is not acted upon
@@ -781,7 +781,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
// GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// THEN the touch is not intercepted
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 1e3197730626..6cc4e44116ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -88,7 +88,6 @@ import com.android.systemui.statusbar.notification.collection.provider.LaunchFul
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
@@ -139,8 +138,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- @Mock
private Handler mHandler;
@Mock
private BubblesManager mBubblesManager;
@@ -163,7 +160,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private InteractionJankMonitor mJankMonitor;
private FakePowerRepository mPowerRepository;
- private PowerInteractor mPowerInteractor;
@Mock
private UserTracker mUserTracker;
private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -214,7 +210,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
UserHandle.of(ActivityManager.getCurrentUser()));
mPowerRepository = new FakePowerRepository();
- mPowerInteractor = PowerInteractorFactory.create(
+ PowerInteractor mPowerInteractor = PowerInteractorFactory.create(
mPowerRepository,
new FalsingCollectorFake(),
mScreenOffAnimationController,
@@ -247,7 +243,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(NotificationLockscreenUserManager.class),
mShadeController,
mKeyguardStateController,
- mNotificationInterruptStateProvider,
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 53c621d24601..bbdc9ced57ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,22 +16,28 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
@@ -55,7 +61,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -64,10 +73,14 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.util.List;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper()
@@ -76,18 +89,23 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
mock(VisualInterruptionDecisionProvider.class);
private NotificationInterruptSuppressor mInterruptSuppressor;
+ private VisualInterruptionCondition mAlertsDisabledCondition;
+ private VisualInterruptionCondition mVrModeCondition;
+ private VisualInterruptionFilter mNeedsRedactionFilter;
+ private VisualInterruptionCondition mPanelsDisabledCondition;
private CommandQueue mCommandQueue;
- private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
private final NotificationAlertsInteractor mNotificationAlertsInteractor =
mock(NotificationAlertsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
mock(KeyguardStateController.class);
- private final InitController mInitController = new InitController();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@Before
public void setup() {
- mMetricsLogger = new FakeMetricsLogger();
mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
mDependency.injectTestDependency(StatusBarStateController.class,
mock(SysuiStatusBarStateController.class));
@@ -95,15 +113,182 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
- NotificationShadeWindowView notificationShadeWindowView =
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
+ }
+
+ @Test
+ public void testInit_refactorDisabled() {
+ ensureRefactorDisabledState();
+ }
+
+ @Test
+ public void testInit_refactorEnabled() {
+ ensureRefactorEnabledState();
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_default_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_default_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ assertFalse(mVrModeCondition.shouldSuppress());
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testPanelsDisabledConditionSuppressesPeek() {
+ ensureRefactorEnabledState();
+
+ final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
+
+ final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions",
+ mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mAlertsDisabledCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertTrue(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ private void createPresenter() {
+ final ShadeViewController shadeViewController = mock(ShadeViewController.class);
+
+ final NotificationShadeWindowView notificationShadeWindowView =
mock(NotificationShadeWindowView.class);
+ when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
NotificationStackScrollLayoutController stackScrollLayoutController =
mock(NotificationStackScrollLayoutController.class);
when(stackScrollLayoutController.getView()).thenReturn(
mock(NotificationStackScrollLayout.class));
- when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
- ShadeViewController shadeViewController = mock(ShadeViewController.class);
+ final InitController initController = new InitController();
+
mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
mContext,
shadeViewController,
@@ -125,110 +310,76 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotifShadeEventSource.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
- mInitController,
+ initController,
mVisualInterruptionDecisionProvider,
mock(NotificationRemoteInputManager.class),
mock(NotificationRemoteInputManager.Callback.class),
mock(NotificationListContainer.class));
- mInitController.executePostInitTasks();
- ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
- ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
- verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
- mInterruptSuppressor = suppressorCaptor.getValue();
+
+ initController.executePostInitTasks();
}
- @Test
- public void testNoSuppressHeadsUp_default() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
+ private void verifyAndCaptureSuppressors() {
+ mInterruptSuppressor = null;
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionCondition.class);
+ verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
+ conditionCaptor.capture());
+ final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
+ mAlertsDisabledCondition = conditions.get(0);
+ mVrModeCondition = conditions.get(1);
+ mPanelsDisabledCondition = conditions.get(2);
+
+ final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionFilter.class);
+ verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
+ mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
}
- @Test
- public void testSuppressHeadsUp_disabledStatusBar() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
+ private void verifyAndCaptureLegacySuppressor() {
+ mAlertsDisabledCondition = null;
+ mVrModeCondition = null;
+ mNeedsRedactionFilter = null;
+ mPanelsDisabledCondition = null;
- assertTrue("The panel should suppress heads up while disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+ ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
+ verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
+ mInterruptSuppressor = suppressorCaptor.getValue();
}
- @Test
- public void testSuppressHeadsUp_disabledNotificationShade() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
+ private void ensureRefactorDisabledState() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
+ createPresenter();
+ verifyAndCaptureLegacySuppressor();
+ }
- assertTrue("The panel should suppress interruptions while notification shade "
- + "disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ private void ensureRefactorEnabledState() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
+ createPresenter();
+ verifyAndCaptureSuppressors();
}
- @Test
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard() {
- Notification n = new Notification.Builder(getContext(), "a")
- .setFullScreenIntent(mock(PendingIntent.class), true)
- .build();
- NotificationEntry entry = new NotificationEntryBuilder()
+ private NotificationEntry createNotificationEntry() {
+ return new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
- .setNotification(n)
+ .setNotification(new Notification.Builder(getContext(), "a").build())
.build();
-
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
- @Test
- public void testSuppressInterruptions_vrMode() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
+ private NotificationEntry createFsiNotificationEntry() {
+ final Notification notification = new Notification.Builder(getContext(), "a")
+ .setFullScreenIntent(mock(PendingIntent.class), true)
.build();
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions",
- mInterruptSuppressor.suppressAwakeInterruptions(entry));
- }
- @Test
- public void testSuppressInterruptions_statusBarAlertsDisabled() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
+ return new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
- .setNotification(n)
+ .setNotification(notification)
.build();
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mInterruptSuppressor.suppressInterruptions(entry));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 9aafee4770de..6a0375d55a72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
@@ -82,14 +80,9 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
@Mock
private lateinit var handler: Handler
- private lateinit var featureFlags: FakeFeatureFlags
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags = FakeFeatureFlags().apply {
- set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, false)
- }
controller = UnlockedScreenOffAnimationController(
context,
wakefulnessLifecycle,
@@ -102,7 +95,6 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
interactionJankMonitor,
powerManager,
handler = handler,
- featureFlags,
)
controller.initialize(centralSurfaces, shadeViewController, lightRevealScrim)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d1b9b8aae70e..0b87fe8da184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -703,6 +704,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mKeyguardStateController,
mShadeViewController,
mStatusBarStateController,
+ mock(StatusBarIconViewBindingFailureTracker.class),
mCommandQueue,
mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index e91b0c1a9a6d..1fb6e2c7a232 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -25,6 +25,8 @@ import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
@@ -68,6 +70,9 @@ import org.mockito.Mockito.verify
class FullMobileConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: FullMobileConnectionRepository
+ private val flags =
+ FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
+
private val systemClock = FakeSystemClock()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -690,6 +695,7 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
testDispatcher,
logger = mock(),
tableLogBuffer,
+ flags,
testScope.backgroundScope,
)
whenever(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index a90bd48a5bce..9d6f3156f83e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -35,7 +35,9 @@ import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
+import android.telephony.TelephonyCallback.DisplayInfoListener
import android.telephony.TelephonyCallback.ServiceStateListener
+import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
@@ -65,6 +67,8 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
@@ -111,6 +115,9 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
+ private val flags =
+ FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
+
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@@ -158,6 +165,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
testDispatcher,
logger,
tableLogger,
+ flags,
testScope.backgroundScope,
)
}
@@ -610,8 +618,80 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
@Test
- fun roaming_gsm_queriesServiceState() =
+ fun roaming_gsm_queriesDisplayInfo_viaDisplayInfo() =
testScope.runTest {
+ // GIVEN flag is true
+ flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
+
+ // Re-create the repository, because the flag is read at init
+ underTest =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+
+ var latest: Boolean? = null
+ val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+
+ val cb = getTelephonyCallbackForType<DisplayInfoListener>()
+
+ // CDMA roaming is off, GSM roaming is off
+ whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
+ cb.onDisplayInfoChanged(
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false)
+ )
+
+ assertThat(latest).isFalse()
+
+ // CDMA roaming is off, GSM roaming is on
+ cb.onDisplayInfoChanged(
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true)
+ )
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun roaming_gsm_queriesDisplayInfo_viaServiceState() =
+ testScope.runTest {
+ // GIVEN flag is false
+ flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, false)
+
+ // Re-create the repository, because the flag is read at init
+ underTest =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 889f60a08766..2ab8c0a07e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -32,6 +32,8 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
@@ -97,6 +99,9 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
+ private val flags =
+ FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
+
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@@ -139,6 +144,7 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
testDispatcher,
logger,
tableLogger,
+ flags,
testScope.backgroundScope,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 03f300542a6f..07abd275d1ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -44,6 +44,8 @@ import com.android.settingslib.R
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -93,6 +95,9 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper
class MobileConnectionsRepositoryTest : SysuiTestCase() {
+ private val flags =
+ FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
+
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
@@ -189,6 +194,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
logger = logger,
mobileMappingsProxy = mobileMappings,
scope = testScope.backgroundScope,
+ flags = flags,
carrierConfigRepository = carrierConfigRepository,
)
carrierMergedFactory =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
index cf815c27a0bf..ec04da7030b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
@@ -50,10 +50,7 @@ object MobileTelephonyHelpers {
}
fun telephonyDisplayInfo(networkType: Int, overrideNetworkType: Int) =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.networkType).thenReturn(networkType)
- whenever(it.overrideNetworkType).thenReturn(overrideNetworkType)
- }
+ TelephonyDisplayInfo(networkType, overrideNetworkType)
inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
index 3dc7de688446..a80238167b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
@@ -16,12 +16,28 @@
package com.android.systemui.statusbar.pipeline.mobile.util
+import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
/** Fake of [SubscriptionManagerProxy] for easy testing */
class FakeSubscriptionManagerProxy(
/** Set the default data subId to be returned in [getDefaultDataSubscriptionId] */
- var defaultDataSubId: Int = INVALID_SUBSCRIPTION_ID
+ var defaultDataSubId: Int = INVALID_SUBSCRIPTION_ID,
+ var activeSubscriptionInfo: SubscriptionInfo? = null
) : SubscriptionManagerProxy {
override fun getDefaultDataSubscriptionId(): Int = defaultDataSubId
+
+ override fun isValidSubscriptionId(subId: Int): Boolean {
+ return subId > -1
+ }
+
+ override suspend fun getActiveSubscriptionInfo(subId: Int): SubscriptionInfo? {
+ return activeSubscriptionInfo
+ }
+
+ /** Sets the active subscription info. */
+ fun setActiveSubscriptionInfo(subId: Int, isEmbedded: Boolean = false) {
+ activeSubscriptionInfo =
+ SubscriptionInfo.Builder().setId(subId).setEmbedded(isEmbedded).build()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index d33806e131d5..1250228e2d37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy
-import com.android.systemui.flags.FakeFeatureFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -78,13 +77,11 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
private lateinit var view: FrameLayout
private lateinit var testableLooper: TestableLooper
private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
- private lateinit var featureFlags: FakeFeatureFlags
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- featureFlags = FakeFeatureFlags()
view = LayoutInflater.from(context)
.inflate(R.layout.keyguard_qs_user_switch, null) as FrameLayout
@@ -101,7 +98,6 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
dozeParameters,
screenOffAnimationController,
userSwitchDialogController,
- featureFlags,
uiEventLogger)
ViewUtils.attachView(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
new file mode 100644
index 000000000000..004f67934e86
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.data.repository
+
+import android.app.NotificationManager
+import android.provider.Settings
+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.statusbar.policy.ZenModeController
+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
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ZenModeRepositoryImplTest : SysuiTestCase() {
+ @Mock lateinit var zenModeController: ZenModeController
+
+ lateinit var underTest: ZenModeRepositoryImpl
+
+ private val testPolicy = NotificationManager.Policy(0, 1, 0)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = ZenModeRepositoryImpl(zenModeController)
+ }
+
+ @Test
+ fun zenMode_reflectsCurrentControllerState() = runTest {
+ whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ val zenMode by collectLastValue(underTest.zenMode)
+ assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ }
+
+ @Test
+ fun zenMode_updatesWhenControllerStateChanges() = runTest {
+ val zenMode by collectLastValue(underTest.zenMode)
+ runCurrent()
+ whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
+ .onZenChanged(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ }
+
+ @Test
+ fun policy_reflectsCurrentControllerState() {
+ runTest {
+ whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
+ val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
+ assertThat(policy).isEqualTo(testPolicy)
+ }
+ }
+
+ @Test
+ fun policy_updatesWhenControllerStateChanges() = runTest {
+ val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
+ runCurrent()
+ whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
+ withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
+ .onConsolidatedPolicyChanged(testPolicy)
+ assertThat(policy).isEqualTo(testPolicy)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
new file mode 100644
index 000000000000..dbb106264ecd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -0,0 +1,161 @@
+/*
+ * 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.policy.domain.interactor
+
+import android.app.NotificationManager.Policy
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import org.junit.Test
+
+@SmallTest
+class ZenModeInteractorTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ZenModeInteractor> {
+
+ val repository: FakeZenModeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(@BindsInstance test: SysuiTestCase): TestComponent
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerZenModeInteractorTest_TestComponent.factory().create(test = this)
+
+ @Test
+ fun testIsZenModeEnabled_off() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_alarms() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_ALARMS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_importantInterruptions() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_noInterruptions() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_unknown() =
+ testComponent.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.zenMode.value = 4 // this should fail if we ever add another zen mode type
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_noPolicy() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.consolidatedNotificationPolicy.value = null
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOffShadeSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_STATUS_BAR)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun testAreNotificationsHiddenInShade_zenOnShadeSuppressed() =
+ testComponent.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ runCurrent()
+
+ assertThat(hidden).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 1e7e1842fa33..1466d24accee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -21,7 +21,6 @@ import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -33,7 +32,6 @@ import androidx.core.animation.doOnCancel
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription
@@ -42,9 +40,8 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewUiEvent
@@ -94,7 +91,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var uiEventLogger: TemporaryViewUiEventLogger
- private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -131,10 +127,8 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
fakeWakeLockBuilder,
fakeClock,
uiEventLogger,
- featureFlags
)
underTest.start()
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@Test
@@ -494,23 +488,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
)
}
- @Test
- fun displayView_oneWayHapticsEnabled_usesPerformHapticFeedback() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
- val constant: Int = HapticFeedbackConstants.CONFIRM
- underTest.displayView(
- createChipbarInfo(
- Icon.Resource(R.id.check_box, null),
- Text.Loaded("text"),
- endItem = null,
- vibrationEffect = null,
- vibrationConstant = constant
- )
- )
-
- verify(vibratorHelper).performHapticFeedback(any(), eq(constant))
- }
-
/** Regression test for b/266119467. */
@Test
fun displayView_animationFailure_viewsStillBecomeVisible() {
@@ -729,14 +706,12 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
endItem: ChipbarEndItem?,
vibrationEffect: VibrationEffect? = null,
allowSwipeToDismiss: Boolean = false,
- vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
): ChipbarInfo {
return ChipbarInfo(
TintedIcon(startIcon, tint = null),
text,
endItem,
vibrationEffect,
- vibrationConstant,
allowSwipeToDismiss,
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
new file mode 100644
index 000000000000..6a801e01a4a5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.unfold.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.test.TestScope
+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.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+open class UnfoldTransitionInteractorTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+ private val unfoldTransitionRepository =
+ UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+
+ private lateinit var underTest: UnfoldTransitionInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+ }
+
+ @Test
+ fun waitForTransitionFinish_noEvents_doesNotComplete() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isFalse()
+ deferred.cancel()
+ }
+
+ @Test
+ fun waitForTransitionFinish_finishEvent_completes() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+ unfoldTransitionProgressProvider.onTransitionFinished()
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isTrue()
+ deferred.cancel()
+ }
+
+ @Test
+ fun waitForTransitionFinish_otherEvent_doesNotComplete() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+ unfoldTransitionProgressProvider.onTransitionStarted()
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isFalse()
+ deferred.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
new file mode 100644
index 000000000000..6d2f00d50d78
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.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.util.drawable
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class LoopedAnimatable2DrawableWrapperTest : SysuiTestCase() {
+
+ @Mock private lateinit var drawable: AnimatedDrawable
+ @Captor private lateinit var callbackCaptor: ArgumentCaptor<Animatable2.AnimationCallback>
+
+ private lateinit var underTest: LoopedAnimatable2DrawableWrapper
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = LoopedAnimatable2DrawableWrapper.fromDrawable(drawable)
+ }
+
+ @Test
+ fun startAddsTheCallback() {
+ underTest.start()
+
+ verify(drawable).registerAnimationCallback(any())
+ }
+
+ @Test
+ fun stopRemovesTheCallback() {
+ underTest.stop()
+
+ verify(drawable).unregisterAnimationCallback(any())
+ }
+
+ @Test
+ fun animationLooped() {
+ underTest.start()
+ verify(drawable).registerAnimationCallback(capture(callbackCaptor))
+
+ callbackCaptor.value.onAnimationEnd(drawable)
+
+ // underTest.start() + looped start()
+ verify(drawable, times(2)).start()
+ }
+
+ private abstract class AnimatedDrawable : Drawable(), Animatable2
+}
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 7456e00e948d..8c823b2376c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -20,7 +20,6 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
@@ -68,7 +67,6 @@ import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialogController;
@@ -149,14 +147,13 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
};
- private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
private FakeSettings mSecureSettings;
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
- @Before
+ @Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -179,8 +176,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mConfigurationController = new FakeConfigurationController();
- mFeatureFlags = new FakeFeatureFlags();
-
mSecureSettings = new FakeSettings();
when(mLazySecureSettings.get()).thenReturn(mSecureSettings);
@@ -200,7 +195,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags,
mLazySecureSettings);
mDialog.init(0, null);
State state = createShellState();
@@ -328,7 +322,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
@Test
public void testVibrateOnRingerChangedToVibrate() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialSilentState = new State();
initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
@@ -349,30 +342,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
- public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialSilentState = new State();
- initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // change ringer to silent
- mDialog.onStateChangedH(initialSilentState);
-
- // expected: shouldn't call vibrate yet
- verify(mVolumeDialogController, never()).vibrate(any());
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // expected: vibrate method of controller is not used
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testNoVibrateOnRingerInitialization() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = -1;
@@ -390,29 +360,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
- public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = -1;
-
- // ringer not initialized yet:
- mDialog.onStateChangedH(initialUnsetState);
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // shouldn't call vibrate on the controller either
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testSelectVibrateFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -426,27 +376,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
- public void testSelectVibrateFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerVibrate.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_VIBRATE, false);
- }
-
- @Test
public void testSelectMuteFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -460,27 +392,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
- public void testSelectMuteFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerMute.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_SILENT, false);
- }
-
- @Test
public void testSelectNormalFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
mDialog.onStateChangedH(initialUnsetState);
@@ -493,23 +407,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
AudioManager.RINGER_MODE_NORMAL, false);
}
- @Test
- public void testSelectNormalFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerNormal.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
- }
-
/**
* Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
* API does not exist. So we do the next best thing; we check the cached icon id.
@@ -682,7 +579,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
State state = createShellState();
state.ringerModeInternal = ringerMode;
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
mDialog.onStateChangedH(state);
mDialog.show(SHOW_REASON_UNKNOWN);
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 a5806001dd43..df7609c544a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -78,6 +78,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -93,7 +94,6 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
-import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
@@ -112,7 +112,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.model.SysUiState;
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.FakeWindowRootViewComponent;
import com.android.systemui.scene.SceneTestUtils;
@@ -125,10 +124,11 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeWindowLogger;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
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.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationEntryHelper;
@@ -163,6 +163,7 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
+import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -249,8 +250,6 @@ public class BubblesTest extends SysuiTestCase {
private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock
private AuthController mAuthController;
- @Mock
- private ShadeExpansionStateManager mShadeExpansionStateManager;
private SysUiState mSysUiState;
private boolean mSysUiStateBubblesExpanded;
@@ -339,9 +338,11 @@ public class BubblesTest extends SysuiTestCase {
private NotifPipelineFlags mNotifPipelineFlags;
@Mock
private Icon mAppBubbleIcon;
+ @Mock
+ private Display mDefaultDisplay;
- private SceneTestUtils mUtils = new SceneTestUtils(this);
- private TestScope mTestScope = mUtils.getTestScope();
+ private final SceneTestUtils mUtils = new SceneTestUtils(this);
+ private final TestScope mTestScope = mUtils.getTestScope();
private ShadeInteractor mShadeInteractor;
private ShellTaskOrganizer mShellTaskOrganizer;
private TaskViewTransitions mTaskViewTransitions;
@@ -352,7 +353,7 @@ public class BubblesTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
- private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private UserHandle mUser0;
@@ -380,6 +381,7 @@ public class BubblesTest extends SysuiTestCase {
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
when(mNotificationShadeWindowView.getViewTreeObserver())
.thenReturn(mock(ViewTreeObserver.class));
+ when(mWindowManager.getDefaultDisplay()).thenReturn(mDefaultDisplay);
FakeDeviceProvisioningRepository deviceProvisioningRepository =
@@ -388,12 +390,11 @@ public class BubblesTest extends SysuiTestCase {
FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeShadeRepository shadeRepository = new FakeShadeRepository();
- FakePowerRepository powerRepository = new FakePowerRepository();
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
PowerInteractor powerInteractor = new PowerInteractor(
- powerRepository,
- new FalsingCollectorFake(),
+ mUtils.getPowerRepository(),
+ mUtils.falsingCollector(),
mock(ScreenOffAnimationController.class),
mStatusBarStateController);
@@ -402,7 +403,7 @@ public class BubblesTest extends SysuiTestCase {
new SceneContainerRepository(
mTestScope.getBackgroundScope(),
mUtils.fakeSceneContainerConfig()),
- powerRepository,
+ powerInteractor,
mock(SceneLogger.class));
FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
@@ -441,7 +442,7 @@ public class BubblesTest extends SysuiTestCase {
new InWindowLauncherUnlockAnimationRepository(),
mTestScope.getBackgroundScope(),
keyguardTransitionInteractor,
- () -> new FakeKeyguardSurfaceBehindRepository(),
+ FakeKeyguardSurfaceBehindRepository::new,
mock(ActivityManagerWrapper.class)
)
);
@@ -460,23 +461,24 @@ public class BubblesTest extends SysuiTestCase {
new ResourcesSplitShadeStateController();
mShadeInteractor =
- new ShadeInteractor(
+ new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
deviceProvisioningRepository,
new FakeDisableFlagsRepository(),
mDozeParameters,
- sceneContainerFlags,
- () -> sceneInteractor,
keyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
- new SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- splitShadeStateController),
- new FakeShadeRepository()
+ new ShadeInteractorLegacyImpl(
+ mTestScope.getBackgroundScope(), keyguardRepository,
+ new SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ splitShadeStateController),
+ shadeRepository
+ )
);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
@@ -546,7 +548,8 @@ public class BubblesTest extends SysuiTestCase {
mock(UserTracker.class),
mock(DeviceProvisionedController.class),
mock(SystemClock.class),
- fakeGlobalSettings
+ fakeGlobalSettings,
+ new FakeEventLog()
);
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 975555c1a46b..c9964c233d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.policy.BatteryController;
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.util.EventLog;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -52,7 +53,8 @@ public class TestableNotificationInterruptStateProviderImpl
UserTracker userTracker,
DeviceProvisionedController deviceProvisionedController,
SystemClock systemClock,
- GlobalSettings globalSettings) {
+ GlobalSettings globalSettings,
+ EventLog eventLog) {
super(
powerManager,
ambientDisplayConfiguration,
@@ -68,7 +70,8 @@ public class TestableNotificationInterruptStateProviderImpl
userTracker,
deviceProvisionedController,
systemClock,
- globalSettings);
+ globalSettings,
+ eventLog);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index 360aa0f89a46..de310b49b8cc 100644
--- a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/InstanceIdSequenceFake.kt
index 6fbe3ada2406..aae270d2b849 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/InstanceIdSequenceFake.kt
@@ -19,14 +19,10 @@ package com.android.systemui
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
-/**
- * Fake [InstanceId] generator.
- */
+/** Fake [InstanceId] generator. */
class InstanceIdSequenceFake(instanceIdMax: Int) : InstanceIdSequence(instanceIdMax) {
- /**
- * Last id used to generate a [InstanceId]. `-1` if no [InstanceId] has been generated.
- */
+ /** Last id used to generate a [InstanceId]. `-1` if no [InstanceId] has been generated. */
var lastInstanceId = -1
private set
@@ -38,4 +34,4 @@ class InstanceIdSequenceFake(instanceIdMax: Int) : InstanceIdSequence(instanceId
}
return newInstanceIdInternal(lastInstanceId)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index 97e43ad91f53..3724291571d7 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -13,24 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
+import android.content.ContentResolver
import android.content.Context
import android.content.res.Resources
import android.testing.TestableContext
import android.testing.TestableResources
-import com.android.systemui.FakeSystemUiModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
+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 dagger.Binds
import dagger.Module
import dagger.Provides
+import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineStart
@@ -56,6 +62,7 @@ interface SysUITestModule {
@Binds @Application fun bindAppResources(resources: Resources): Resources
@Binds @Main fun bindMainResources(resources: Resources): Resources
@Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher
+ @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor
companion object {
@Provides
@@ -72,6 +79,22 @@ interface SysUITestModule {
@Provides
fun provideFakeBroadcastDispatcher(test: SysuiTestCase): FakeBroadcastDispatcher =
test.fakeBroadcastDispatcher
+
+ @Provides
+ fun provideContentResolver(context: Context): ContentResolver = context.contentResolver
+
+ @Provides
+ fun provideBaseShadeInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
+ sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+ ): BaseShadeInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index fd50f15dc5fc..f57ace9bfdc6 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
@@ -24,7 +24,6 @@ import com.android.internal.logging.MetricsLogger
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
@@ -56,7 +55,9 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.mock
import com.android.wm.shell.bubbles.Bubbles
import dagger.Binds
@@ -101,6 +102,10 @@ data class TestMocksModule(
@get:Provides val keyguardViewController: KeyguardViewController = mock(),
@get:Provides val dialogLaunchAnimator: DialogLaunchAnimator = mock(),
@get:Provides val sysuiState: SysUiState = mock(),
+ @get:Provides
+ val unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider> =
+ Optional.empty(),
+ @get:Provides val zenModeController: ZenModeController = mock(),
// log buffers
@get:[Provides BroadcastDispatcherLog]
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index ddfe79aedce6..45ded7ffcc8c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -20,16 +20,16 @@ import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -37,12 +37,13 @@ import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.currentTime
class FakeAuthenticationRepository(
- private val deviceEntryRepository: FakeDeviceEntryRepository,
private val currentTime: () -> Long,
) : AuthenticationRepository {
- private val _isAutoConfirmEnabled = MutableStateFlow(false)
- override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow()
+ private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ _isAutoConfirmFeatureEnabled.asStateFlow()
+ override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = HINTING_PIN_LENGTH
@@ -59,6 +60,12 @@ class FakeAuthenticationRepository(
override val minPatternLength: Int = 4
+ override val minPasswordLength: Int = 4
+
+ private val _isPinEnhancedPrivacyEnabled = MutableStateFlow(false)
+ override val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ _isPinEnhancedPrivacyEnabled.asStateFlow()
+
private var failedAttemptCount = 0
private var throttlingEndTimestamp = 0L
private var credentialOverride: List<Any>? = null
@@ -79,7 +86,7 @@ class FakeAuthenticationRepository(
override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1
- deviceEntryRepository.setUnlocked(isSuccessful)
+ authenticationChallengeResult.emit(isSuccessful)
}
override suspend fun getPinLength(): Int {
@@ -98,8 +105,8 @@ class FakeAuthenticationRepository(
_throttling.value = throttlingModel
}
- fun setAutoConfirmEnabled(isEnabled: Boolean) {
- _isAutoConfirmEnabled.value = isEnabled
+ fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) {
+ _isAutoConfirmFeatureEnabled.value = isEnabled
}
override suspend fun setThrottleDuration(durationMs: Int) {
@@ -137,6 +144,10 @@ class FakeAuthenticationRepository(
}
}
+ fun setPinEnhancedPrivacyEnabled(isEnabled: Boolean) {
+ _isPinEnhancedPrivacyEnabled.value = isEnabled
+ }
+
private fun getExpectedCredential(securityMode: SecurityMode): List<Any> {
return when (val credentialType = getCurrentCredentialType(securityMode)) {
LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN
@@ -169,6 +180,7 @@ class FakeAuthenticationRepository(
is AuthenticationMethodModel.Password -> SecurityMode.Password
is AuthenticationMethodModel.Pattern -> SecurityMode.Pattern
is AuthenticationMethodModel.None -> SecurityMode.None
+ is AuthenticationMethodModel.Sim -> SecurityMode.SimPin
}
}
@@ -215,9 +227,8 @@ object FakeAuthenticationRepositoryModule {
@Provides
@SysUISingleton
fun provideFake(
- deviceEntryRepository: FakeDeviceEntryRepository,
scope: TestScope,
- ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime })
+ ) = FakeAuthenticationRepository(currentTime = { scope.currentTime })
@Module
interface Bindings {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
index 0c5e43809fab..005cac490d89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
@@ -19,10 +19,15 @@ package com.android.systemui.biometrics.data.repository
import android.hardware.biometrics.SensorLocationInternal
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
+@SysUISingleton
+class FakeFingerprintPropertyRepository @Inject constructor() : FingerprintPropertyRepository {
private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
override val sensorId = _sensorId.asStateFlow()
@@ -50,4 +55,29 @@ class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
_sensorType.value = sensorType
_sensorLocations.value = sensorLocations
}
+
+ /** setProperties as if the device supports UDFPS_OPTICAL. */
+ fun supportsUdfps() {
+ setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+ sensorLocations = emptyMap(),
+ )
+ }
+
+ /** setProperties as if the device supports the rear fingerprint sensor. */
+ fun supportsRearFps() {
+ setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.REAR,
+ sensorLocations = emptyMap(),
+ )
+ }
+}
+
+@Module
+interface FakeFingerprintPropertyRepositoryModule {
+ @Binds fun bindFake(fake: FakeFingerprintPropertyRepository): FingerprintPropertyRepository
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeSimBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeSimBouncerRepository.kt
new file mode 100644
index 000000000000..890e69dced0b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeSimBouncerRepository.kt
@@ -0,0 +1,68 @@
+/*
+ * 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 com.android.systemui.bouncer.data.repository
+
+import android.telephony.SubscriptionInfo
+import com.android.systemui.bouncer.data.model.SimPukInputModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Fakes the SimBouncerRepository. */
+class FakeSimBouncerRepository : SimBouncerRepository {
+ private val _subscriptionId: MutableStateFlow<Int> = MutableStateFlow(-1)
+ override val subscriptionId: StateFlow<Int> = _subscriptionId
+ private val _activeSubscriptionInfo: MutableStateFlow<SubscriptionInfo?> =
+ MutableStateFlow(null)
+ override val activeSubscriptionInfo: StateFlow<SubscriptionInfo?> = _activeSubscriptionInfo
+ private val _isLockedEsim: MutableStateFlow<Boolean?> = MutableStateFlow(null)
+ override val isLockedEsim: StateFlow<Boolean?> = _isLockedEsim
+ private val _isSimPukLocked: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isSimPukLocked: StateFlow<Boolean> = _isSimPukLocked
+ private val _errorDialogMessage: MutableStateFlow<String?> = MutableStateFlow(null)
+ override val errorDialogMessage: StateFlow<String?> = _errorDialogMessage
+ private var _simPukInputModel = SimPukInputModel()
+ override val simPukInputModel: SimPukInputModel
+ get() = _simPukInputModel
+
+ fun setSubscriptionId(subId: Int) {
+ _subscriptionId.value = subId
+ }
+
+ fun setActiveSubscriptionInfo(subscriptioninfo: SubscriptionInfo) {
+ _activeSubscriptionInfo.value = subscriptioninfo
+ }
+
+ fun setLockedEsim(isLockedEsim: Boolean) {
+ _isLockedEsim.value = isLockedEsim
+ }
+
+ fun setSimPukLocked(isSimPukLocked: Boolean) {
+ _isSimPukLocked.value = isSimPukLocked
+ }
+
+ fun setErrorDialogMessage(msg: String?) {
+ _errorDialogMessage.value = msg
+ }
+
+ override fun setSimPukUserInput(enteredSimPuk: String?, enteredSimPin: String?) {
+ _simPukInputModel = SimPukInputModel(enteredSimPuk, enteredSimPin)
+ }
+
+ override fun setSimVerificationErrorMessage(msg: String?) {
+ _errorDialogMessage.value = msg
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
new file mode 100644
index 000000000000..7207948412b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.broadcast
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.broadcastDispatcher by
+ Kosmos.Fixture {
+ FakeBroadcastDispatcher(
+ context = mock(),
+ mainExecutor = mock(),
+ broadcastRunningLooper = mock(),
+ broadcastRunningExecutor = mock(),
+ dumpManager = mock(),
+ logger = mock(),
+ userTracker = mock(),
+ shouldFailOnLeakedReceiver = false
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index d0fa27e3b79a..6b38d6ea315a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.common.ui.data.repository
+import android.content.res.Configuration
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -36,6 +37,10 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor
MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val onConfigurationChange: Flow<Unit> = _onConfigurationChange.asSharedFlow()
+ private val _configurationChangeValues = MutableSharedFlow<Configuration>()
+ override val configurationValues: Flow<Configuration> =
+ _configurationChangeValues.asSharedFlow()
+
private val _scaleForResolution = MutableStateFlow(1f)
override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
@@ -49,6 +54,11 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor
_onConfigurationChange.tryEmit(Unit)
}
+ fun onConfigurationChange(configChange: Configuration) {
+ _configurationChangeValues.tryEmit(configChange)
+ onAnyConfigurationChange()
+ }
+
fun setScaleForResolution(scale: Float) {
_scaleForResolution.value = scale
}
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 8a2ff50d8a32..c6f12e2014b3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,22 +1,14 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of [CommunalWidgetRepository] */
class FakeCommunalWidgetRepository : CommunalWidgetRepository {
- private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
- override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
-
private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
- fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
- _stopwatchAppWidgetInfo.value = appWidgetInfo
- }
-
fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
_communalWidgets.value = inventory
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 0c821eab65e0..faacce64b2e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -22,6 +22,7 @@ import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -40,6 +41,7 @@ object CommunalInteractorFactory {
smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
appWidgetHost: AppWidgetHost = mock(),
+ editWidgetsActivityStarter: EditWidgetsActivityStarter = mock(),
): WithDependencies {
val withDeps =
CommunalTutorialInteractorFactory.create(
@@ -57,13 +59,15 @@ object CommunalInteractorFactory {
withDeps.keyguardInteractor,
withDeps.communalTutorialInteractor,
appWidgetHost,
+ editWidgetsActivityStarter,
CommunalInteractor(
communalRepository,
widgetRepository,
mediaRepository,
smartspaceRepository,
- withDeps.communalTutorialInteractor,
+ withDeps.keyguardInteractor,
appWidgetHost,
+ editWidgetsActivityStarter,
),
)
}
@@ -78,6 +82,7 @@ object CommunalInteractorFactory {
val keyguardInteractor: KeyguardInteractor,
val tutorialInteractor: CommunalTutorialInteractor,
val appWidgetHost: AppWidgetHost,
+ val editWidgetsActivityStarter: EditWidgetsActivityStarter,
val communalInteractor: CommunalInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
index 36f088214153..8c653a535e29 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
@@ -26,12 +26,14 @@ import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule
import com.android.systemui.statusbar.data.FakeStatusBarDataLayerModule
import com.android.systemui.telephony.data.FakeTelephonyDataLayerModule
import com.android.systemui.user.data.FakeUserDataLayerModule
+import com.android.systemui.util.animation.data.FakeAnimationUtilDataLayerModule
import dagger.Module
@Module(
includes =
[
FakeAccessibilityDataLayerModule::class,
+ FakeAnimationUtilDataLayerModule::class,
FakeAuthenticationDataLayerModule::class,
FakeBouncerDataLayerModule::class,
FakeCommonDataLayerModule::class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
index 44286b715abb..8ff04a63802a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -15,17 +15,23 @@
package com.android.systemui.deviceentry.data
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeTrustRepositoryModule
import dagger.Module
@Module(
includes =
[
+ FakeBiometricSettingsRepositoryModule::class,
FakeDeviceEntryRepositoryModule::class,
- FakeTrustRepositoryModule::class,
FakeDeviceEntryFaceAuthRepositoryModule::class,
+ FakeDeviceEntryFingerprintAuthRepositoryModule::class,
+ FakeFingerprintPropertyRepositoryModule::class,
+ FakeTrustRepositoryModule::class,
]
)
object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index f0293489cb87..ba70d46fd954 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
- private var isInsecureLockscreenEnabled = true
+ private var isLockscreenEnabled = true
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled
@@ -35,16 +35,20 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
- override suspend fun isInsecureLockscreenEnabled(): Boolean {
- return isInsecureLockscreenEnabled
+ override suspend fun isLockscreenEnabled(): Boolean {
+ return isLockscreenEnabled
+ }
+
+ override fun reportSuccessfulAuthentication() {
+ _isUnlocked.value = true
}
fun setUnlocked(isUnlocked: Boolean) {
_isUnlocked.value = isUnlocked
}
- fun setInsecureLockscreenEnabled(isLockscreenEnabled: Boolean) {
- this.isInsecureLockscreenEnabled = isLockscreenEnabled
+ fun setLockscreenEnabled(isLockscreenEnabled: Boolean) {
+ this.isLockscreenEnabled = isLockscreenEnabled
}
fun setBypassEnabled(isBypassEnabled: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
new file mode 100644
index 000000000000..a78076338c79
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.flags
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index 852611230623..df31a12b8415 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -18,13 +18,18 @@
package com.android.systemui.keyguard.data.repository
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-class FakeBiometricSettingsRepository : BiometricSettingsRepository {
+@SysUISingleton
+class FakeBiometricSettingsRepository @Inject constructor() : BiometricSettingsRepository {
private val _isFingerprintEnrolledAndEnabled = MutableStateFlow(false)
override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean>
get() = _isFingerprintEnrolledAndEnabled
@@ -97,3 +102,8 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
}
}
}
+
+@Module
+interface FakeBiometricSettingsRepositoryModule {
+ @Binds fun bindFake(fake: FakeBiometricSettingsRepository): BiometricSettingsRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 38791caf5bfc..c9160efb75a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -17,14 +17,20 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
-class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
+@SysUISingleton
+class FakeDeviceEntryFingerprintAuthRepository @Inject constructor() :
+ DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
fun setLockedOut(lockedOut: Boolean) {
@@ -52,3 +58,11 @@ class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepos
_authenticationStatus.value = status
}
}
+
+@Module
+interface FakeDeviceEntryFingerprintAuthRepositoryModule {
+ @Binds
+ fun bindFake(
+ fake: FakeDeviceEntryFingerprintAuthRepository
+ ): DeviceEntryFingerprintAuthRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index d3744d55c55d..4068e408f0bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -167,6 +167,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_isKeyguardOccluded.value = isOccluded
}
+ fun setKeyguardUnlocked(isUnlocked: Boolean) {
+ _isKeyguardUnlocked.value = isUnlocked
+ }
+
override fun setIsDozing(isDozing: Boolean) {
_isDozing.value = isDozing
}
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 b90ad8cd8745..a94ca291298d 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
@@ -40,7 +40,7 @@ import kotlinx.coroutines.test.runCurrent
class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitionRepository {
private val _transitions =
- MutableSharedFlow<TransitionStep>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ MutableSharedFlow<TransitionStep>(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val transitions: SharedFlow<TransitionStep> = _transitions
init {
@@ -130,7 +130,7 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
* only a FINISHED step, override [validateStep].
*/
suspend fun sendTransitionStep(step: TransitionStep, validateStep: Boolean = true) {
- _transitions.replayCache.getOrNull(0)?.let { lastStep ->
+ _transitions.replayCache.last().let { lastStep ->
if (
validateStep &&
step.transitionState == TransitionState.FINISHED &&
@@ -151,6 +151,17 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
_transitions.emit(step)
}
+ suspend fun sendTransitionSteps(
+ steps: List<TransitionStep>,
+ testScope: TestScope,
+ validateStep: Boolean = true
+ ) {
+ steps.forEach {
+ sendTransitionStep(it, validateStep = validateStep)
+ testScope.testScheduler.runCurrent()
+ }
+ }
+
override fun startTransition(info: TransitionInfo): UUID? {
return if (info.animator == null) UUID.randomUUID() else null
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index fc34903e8a1d..6c2ce7139375 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.os.Handler
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -86,9 +87,11 @@ object KeyguardDismissInteractorFactory {
mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
bouncerRepository,
+ FakeFingerprintPropertyRepository(),
FakeBiometricSettingsRepository(),
FakeSystemClock(),
keyguardUpdateMonitor,
+ testScope.backgroundScope,
)
val powerInteractorWithDeps =
PowerInteractorFactory.create(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index cc843b536756..b05915c4f678 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -1,8 +1,16 @@
package com.android.systemui.kosmos
+import android.content.Context
+import android.os.UserManager
import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import org.mockito.Mockito
-val Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
-val Kosmos.testScope by Fixture { TestScope(testDispatcher) }
+var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
+var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
+var Kosmos.context by Fixture<Context>()
+var Kosmos.lifecycleScope by Fixture<CoroutineScope>()
+
+val Kosmos.userManager by Fixture { Mockito.mock(UserManager::class.java) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QsEventLoggerFake.kt
index 40aa2607dc94..baccc6f6962f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QsEventLoggerFake.kt
@@ -5,7 +5,7 @@
* you may not use this 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,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
new file mode 100644
index 000000000000..1cb2587e4e99
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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
+
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.InstanceIdSequenceFake
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.instanceIdSequenceFake: InstanceIdSequenceFake by
+ Kosmos.Fixture { InstanceIdSequenceFake(0) }
+val Kosmos.uiEventLogger: UiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() }
+val Kosmos.qsEventLogger: QsEventLoggerFake by
+ Kosmos.Fixture { QsEventLoggerFake(uiEventLogger, instanceIdSequenceFake) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
index 1efa74b0551a..62765d10486c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
@@ -20,7 +20,6 @@ import android.os.UserHandle
class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
- var handleResult: Boolean = false
var policyResult: DisabledByPolicyInteractor.PolicyResult =
DisabledByPolicyInteractor.PolicyResult.TileEnabled
@@ -31,5 +30,9 @@ class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
override fun handlePolicyResult(
policyResult: DisabledByPolicyInteractor.PolicyResult
- ): Boolean = handleResult
+ ): Boolean =
+ when (policyResult) {
+ is DisabledByPolicyInteractor.PolicyResult.TileEnabled -> false
+ is DisabledByPolicyInteractor.PolicyResult.TileDisabled -> true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
index 2b3330f3a33b..3fcf8a93dc87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -17,16 +17,21 @@
package com.android.systemui.qs.tiles.base.interactor
import android.os.UserHandle
-import javax.annotation.CheckReturnValue
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
-class FakeQSTileDataInteractor<T>(
- private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = Int.MAX_VALUE),
- private val availabilityFlow: MutableSharedFlow<Boolean> =
- MutableSharedFlow(replay = Int.MAX_VALUE),
-) : QSTileDataInteractor<T> {
+class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
+
+ private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = 1)
+ val dataSubscriptionCount
+ get() = dataFlow.subscriptionCount
+ private val availabilityFlow: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
+ val availabilitySubscriptionCount
+ get() = availabilityFlow.subscriptionCount
+
+ private val mutableTriggers = mutableListOf<DataUpdateTrigger>()
+ val triggers: List<DataUpdateTrigger> = mutableTriggers
private val mutableDataRequests = mutableListOf<DataRequest>()
val dataRequests: List<DataRequest> = mutableDataRequests
@@ -34,14 +39,17 @@ class FakeQSTileDataInteractor<T>(
private val mutableAvailabilityRequests = mutableListOf<AvailabilityRequest>()
val availabilityRequests: List<AvailabilityRequest> = mutableAvailabilityRequests
- @CheckReturnValue fun emitData(data: T): Boolean = dataFlow.tryEmit(data)
+ suspend fun emitData(data: T): Unit = dataFlow.emit(data)
fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
mutableDataRequests.add(DataRequest(user))
- return triggers.flatMapLatest { dataFlow }
+ return triggers.flatMapLatest {
+ mutableTriggers.add(it)
+ dataFlow
+ }
}
override fun availability(user: UserHandle): Flow<Boolean> {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/flashlight/FlashlightTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/flashlight/FlashlightTileKosmos.kt
new file mode 100644
index 000000000000..97e9b519b022
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/flashlight/FlashlightTileKosmos.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.qs.tiles.impl.flashlight
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.statusbar.policy.PolicyModule
+
+val Kosmos.qsFlashlightTileConfig by
+ Kosmos.Fixture { PolicyModule.provideFlashlightTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
index de72a7dc30d7..d231d63a3906 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
@@ -24,6 +24,8 @@ class FakeQSTileConfigProvider : QSTileConfigProvider {
override fun getConfig(tileSpec: String): QSTileConfig = configs.getValue(tileSpec)
+ override fun hasConfig(tileSpec: String): Boolean = configs.containsKey(tileSpec)
+
fun putConfig(tileSpec: TileSpec, config: QSTileConfig) {
configs[tileSpec.spec] = config
}
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
new file mode 100644
index 000000000000..2902c3f65a16
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.ui.adapter
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+
+class FakeQSSceneAdapter(
+ private val inflateDelegate: suspend (Context, ViewGroup?) -> View,
+) : QSSceneAdapter {
+ private val _customizing = MutableStateFlow(false)
+ override val isCustomizing: StateFlow<Boolean> = _customizing.asStateFlow()
+
+ private val _view = MutableStateFlow<View?>(null)
+ override val qsView: Flow<View> = _view.filterNotNull()
+
+ private val _state = MutableStateFlow<QSSceneAdapter.State?>(null)
+ val state = _state.filterNotNull()
+
+ override suspend fun inflate(context: Context, parent: ViewGroup?) {
+ _view.value = inflateDelegate(context, parent)
+ }
+
+ override fun setState(state: QSSceneAdapter.State) {
+ if (_view.value != null) {
+ _state.value = state
+ }
+ }
+
+ fun setCustomizing(value: Boolean) {
+ _customizing.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 36ec18fc4de4..80c38b2d228c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -23,20 +23,24 @@ import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.telecom.TelecomManager
+import android.telephony.PinResult
+import android.telephony.PinResult.PIN_RESULT_TYPE_SUCCESS
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
import com.android.internal.logging.MetricsLogger
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.FakeSimBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.EmergencyDialerIntentFactory
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -59,11 +63,16 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.kosmos.Kosmos
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.data.repository.PowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -71,6 +80,11 @@ import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.data.repository.TelephonyRepository
@@ -87,6 +101,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.currentTime
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito
/**
* Utilities for creating scene container framework related repositories, interactors, and
@@ -105,12 +122,12 @@ class SceneTestUtils(
FakeFeatureFlagsClassic().apply {
set(Flags.FACE_AUTH_REFACTOR, false)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.NSSL_DEBUG_LINES, false)
}
val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true }
val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
val authenticationRepository: FakeAuthenticationRepository by lazy {
FakeAuthenticationRepository(
- deviceEntryRepository = deviceEntryRepository,
currentTime = { testScope.currentTime },
)
}
@@ -126,9 +143,33 @@ class SceneTestUtils(
}
val telephonyRepository: FakeTelephonyRepository by lazy { FakeTelephonyRepository() }
+ val bouncerRepository = BouncerRepository(featureFlags)
val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
+ val simBouncerRepository: FakeSimBouncerRepository by lazy { FakeSimBouncerRepository() }
+ val telephonyManager: TelephonyManager =
+ Mockito.mock(TelephonyManager::class.java).apply {
+ whenever(createForSubscriptionId(anyInt())).thenReturn(this)
+ whenever(supplyIccLockPin(anyString()))
+ .thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
+ }
+ val mobileConnectionsRepository: FakeMobileConnectionsRepository by lazy {
+ FakeMobileConnectionsRepository(mock(), mock())
+ }
+
+ val simBouncerInteractor =
+ SimBouncerInteractor(
+ applicationContext = context,
+ backgroundDispatcher = testDispatcher,
+ applicationScope = applicationScope(),
+ repository = simBouncerRepository,
+ telephonyManager = telephonyManager,
+ resources = context.resources,
+ keyguardUpdateMonitor = mock(),
+ euiccManager = context.getSystemService(Context.EUICC_SERVICE) as EuiccManager,
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ )
val userRepository: UserRepository by lazy {
FakeUserRepository().apply {
@@ -140,6 +181,7 @@ class SceneTestUtils(
private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
private var falsingInteractor: FalsingInteractor? = null
+ private var powerInteractor: PowerInteractor? = null
fun fakeSceneContainerRepository(
containerConfig: SceneContainerConfig = fakeSceneContainerConfig(),
@@ -162,7 +204,7 @@ class SceneTestUtils(
return SceneInteractor(
applicationScope = applicationScope(),
repository = repository,
- powerRepository = powerRepository,
+ powerInteractor = powerInteractor(),
logger = mock(),
)
}
@@ -181,6 +223,7 @@ class SceneTestUtils(
sceneInteractor = sceneInteractor,
deviceEntryFaceAuthRepository = faceAuthRepository,
trustRepository = trustRepository,
+ flags = FakeSceneContainerFlags(enabled = true)
)
}
@@ -192,7 +235,6 @@ class SceneTestUtils(
repository = repository,
backgroundDispatcher = testDispatcher,
userRepository = userRepository,
- deviceEntryRepository = deviceEntryRepository,
clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } }
)
}
@@ -221,19 +263,32 @@ class SceneTestUtils(
}
fun bouncerInteractor(
- deviceEntryInteractor: DeviceEntryInteractor,
authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
+ keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor = mock(),
): BouncerInteractor {
return BouncerInteractor(
applicationScope = applicationScope(),
applicationContext = context,
- repository = BouncerRepository(featureFlags),
- deviceEntryInteractor = deviceEntryInteractor,
+ repository = bouncerRepository,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
+ keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
flags = sceneContainerFlags,
falsingInteractor = falsingInteractor(),
+ powerInteractor = powerInteractor(),
+ simBouncerInteractor = simBouncerInteractor,
+ )
+ }
+
+ fun keyguardRootViewModel(): KeyguardRootViewModel = mock()
+
+ fun notificationsPlaceholderViewModel(): NotificationsPlaceholderViewModel {
+ return NotificationsPlaceholderViewModel(
+ interactor =
+ NotificationStackAppearanceInteractor(
+ repository = NotificationStackAppearanceRepository(),
+ ),
+ flags = sceneContainerFlags,
+ featureFlags = featureFlags,
)
}
@@ -254,6 +309,7 @@ class SceneTestUtils(
users = flowOf(users),
userSwitcherMenu = flowOf(createMenuActions()),
actionButtonInteractor = actionButtonInteractor,
+ simBouncerInteractor = simBouncerInteractor,
)
}
@@ -271,6 +327,22 @@ class SceneTestUtils(
return falsingCollectorFake
}
+ fun powerInteractor(
+ repository: PowerRepository = powerRepository,
+ falsingCollector: FalsingCollector = falsingCollector(),
+ screenOffAnimationController: ScreenOffAnimationController = mock(),
+ statusBarStateController: StatusBarStateController = mock(),
+ ): PowerInteractor {
+ return powerInteractor
+ ?: PowerInteractor(
+ repository = repository,
+ falsingCollector = falsingCollector,
+ screenOffAnimationController = screenOffAnimationController,
+ statusBarStateController = statusBarStateController,
+ )
+ .also { powerInteractor = it }
+ }
+
private fun applicationScope(): CoroutineScope {
return testScope.backgroundScope
}
@@ -345,19 +417,4 @@ class SceneTestUtils(
dozeLogger = dozeLogger,
)
}
-
- companion object {
- fun DomainLayerAuthenticationMethodModel.toDataLayer(): DataLayerAuthenticationMethodModel {
- return when (this) {
- DomainLayerAuthenticationMethodModel.None -> DataLayerAuthenticationMethodModel.None
- DomainLayerAuthenticationMethodModel.Swipe ->
- DataLayerAuthenticationMethodModel.None
- DomainLayerAuthenticationMethodModel.Pin -> DataLayerAuthenticationMethodModel.Pin
- DomainLayerAuthenticationMethodModel.Password ->
- DataLayerAuthenticationMethodModel.Password
- DomainLayerAuthenticationMethodModel.Pattern ->
- DataLayerAuthenticationMethodModel.Pattern
- }
- }
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 800593fe61a1..92ec4f22001b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -59,11 +59,22 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
private val _legacyIsQsExpanded = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+ override val legacyLockscreenShadeTracking = MutableStateFlow(false)
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
_legacyIsQsExpanded.value = legacyIsQsExpanded
}
+ private val _legacyExpandImmediate = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandImmediate = _legacyExpandImmediate
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyExpandImmediate(legacyExpandImmediate: Boolean) {
+ _legacyExpandImmediate.value = legacyExpandImmediate
+ }
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyExpandedOrAwaitingInputTransfer(
legacyExpandedOrAwaitingInputTransfer: Boolean
@@ -81,6 +92,19 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
_legacyShadeTracking.value = tracking
}
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyLockscreenShadeTracking(tracking: Boolean) {
+ legacyLockscreenShadeTracking.value = tracking
+ }
+
+ private val _legacyQsFullscreen = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead") override val legacyQsFullscreen = _legacyQsFullscreen
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyQsFullscreen(legacyQsFullscreen: Boolean) {
+ _legacyQsFullscreen.value = legacyQsFullscreen
+ }
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
new file mode 100644
index 000000000000..9851b0ef9e94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.data.model
+
+import android.graphics.drawable.Icon
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+
+/** Simple ActiveNotificationModel builder for use in tests. */
+fun activeNotificationModel(
+ key: String,
+ groupKey: String? = null,
+ isAmbient: Boolean = false,
+ isRowDismissed: Boolean = false,
+ isSilent: Boolean = false,
+ isLastMessageFromReply: Boolean = false,
+ isSuppressedFromStatusBar: Boolean = false,
+ isPulsing: Boolean = false,
+ aodIcon: Icon? = null,
+ shelfIcon: Icon? = null,
+ statusBarIcon: Icon? = null,
+) =
+ ActiveNotificationModel(
+ key = key,
+ groupKey = groupKey,
+ isAmbient = isAmbient,
+ isRowDismissed = isRowDismissed,
+ isSilent = isSilent,
+ isLastMessageFromReply = isLastMessageFromReply,
+ isSuppressedFromStatusBar = isSuppressedFromStatusBar,
+ isPulsing = isPulsing,
+ aodIcon = aodIcon,
+ shelfIcon = shelfIcon,
+ statusBarIcon = statusBarIcon,
+ )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt
new file mode 100644
index 000000000000..cb1ba206d110
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+
+/**
+ * Make the repository hold [count] active notifications for testing. The keys of the notifications
+ * are "0", "1", ..., (count - 1).toString().
+ */
+fun ActiveNotificationListRepository.setActiveNotifs(count: Int) {
+ this.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { repeat(count) { i -> addEntry(activeNotificationModel(key = i.toString())) } }
+ .build()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index a9c8ec7dcb7d..a9c8ec7dcb7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index cce038f4ffc1..cce038f4ffc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
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
new file mode 100644
index 000000000000..0c2b115a8af5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.statusbar.policy
+
+class FakeDeviceProvisionedController : DeviceProvisionedController {
+ @JvmField var deviceProvisioned = true
+
+ override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun isDeviceProvisioned() = deviceProvisioned
+
+ override fun getCurrentUser(): Int {
+ TODO("Not yet implemented")
+ }
+
+ override fun isUserSetup(user: Int): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun isCurrentUserSetup(): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun isFrpActive(): Boolean {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
index 5aece1bbbd31..16dab40d6edc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
@@ -16,7 +16,14 @@
package com.android.systemui.statusbar.policy.data
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule
+import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepositoryModule
import dagger.Module
-@Module(includes = [FakeDeviceProvisioningRepositoryModule::class])
+@Module(
+ includes =
+ [
+ FakeDeviceProvisioningRepositoryModule::class,
+ FakeZenModeRepositoryModule::class,
+ ]
+)
object FakeStatusBarPolicyDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
new file mode 100644
index 000000000000..c4d78678e59a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.policy.data.repository
+
+import android.app.NotificationManager.Policy
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeZenModeRepository @Inject constructor() : ZenModeRepository {
+ override val zenMode: MutableStateFlow<Int> = MutableStateFlow(Settings.Global.ZEN_MODE_OFF)
+ override val consolidatedNotificationPolicy: MutableStateFlow<Policy?> =
+ MutableStateFlow(
+ Policy(
+ /* priorityCategories = */ 0,
+ /* priorityCallSenders = */ 0,
+ /* priorityMessageSenders = */ 0,
+ )
+ )
+
+ fun setSuppressedVisualEffects(suppressedVisualEffects: Int) {
+ consolidatedNotificationPolicy.value =
+ Policy(
+ /* priorityCategories = */ 0,
+ /* priorityCallSenders = */ 0,
+ /* priorityMessageSenders = */ 0,
+ /* suppressedVisualEffects = */ suppressedVisualEffects,
+ )
+ }
+}
+
+@Module
+interface FakeZenModeRepositoryModule {
+ @Binds fun bindFake(fake: FakeZenModeRepository): ZenModeRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt
new file mode 100644
index 000000000000..8bce9b6d461d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.user.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.userRepository by Kosmos.Fixture { FakeUserRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt
new file mode 100644
index 000000000000..e69570433d43
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.user.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.context
+import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.userManager
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.guestUserInteractor by
+ Kosmos.Fixture {
+ GuestUserInteractor(
+ applicationContext = context,
+ applicationScope = lifecycleScope,
+ mainDispatcher = testDispatcher,
+ backgroundDispatcher = testDispatcher,
+ manager = userManager,
+ deviceProvisionedController = mock(),
+ repository = userRepository,
+ devicePolicyManager = mock(),
+ refreshUsersScheduler = refreshUsersScheduler,
+ uiEventLogger = mock(),
+ resumeSessionReceiver = mock(),
+ resetOrExitSessionReceiver = mock(),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt
new file mode 100644
index 000000000000..87a2fe0249e3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.user.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.refreshUsersScheduler by
+ Kosmos.Fixture {
+ RefreshUsersScheduler(
+ applicationScope = lifecycleScope,
+ mainDispatcher = testDispatcher,
+ repository = userRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
new file mode 100644
index 000000000000..6d6b2683a7ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user.domain.interactor
+
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.flags.featureFlags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.context
+import com.android.systemui.kosmos.lifecycleScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.userManager
+import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
+import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.userSwitcherInteractor by
+ Kosmos.Fixture {
+ UserSwitcherInteractor(
+ applicationContext = context,
+ repository = userRepository,
+ activityStarter = mock(),
+ keyguardInteractor =
+ KeyguardInteractorFactory.create(featureFlags = featureFlags).keyguardInteractor,
+ featureFlags = featureFlags,
+ manager = userManager,
+ headlessSystemUserMode = mock(),
+ applicationScope = lifecycleScope,
+ telephonyInteractor =
+ TelephonyInteractor(
+ repository = FakeTelephonyRepository(),
+ ),
+ broadcastDispatcher = broadcastDispatcher,
+ keyguardUpdateMonitor = mock(),
+ backgroundDispatcher = testDispatcher,
+ activityManager = mock(),
+ refreshUsersScheduler = refreshUsersScheduler,
+ guestUserInteractor = guestUserInteractor,
+ uiEventLogger = mock(),
+ userRestrictionChecker = mock()
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeEventLog.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeEventLog.kt
new file mode 100644
index 000000000000..ea2eeabf72eb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeEventLog.kt
@@ -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.systemui.util
+
+/** A fake [com.android.systemui.util.EventLog] for tests. */
+class FakeEventLog : EventLog {
+ data class Event(val tag: Int, val value: Any)
+
+ private val _events: MutableList<Event> = mutableListOf()
+ val events: List<Event>
+ get() = _events
+
+ fun clear() {
+ _events.clear()
+ }
+
+ override fun writeEvent(tag: Int, value: Int): Int {
+ _events.add(Event(tag, value))
+ return 1
+ }
+
+ override fun writeEvent(tag: Int, value: Long): Int {
+ _events.add(Event(tag, value))
+ return 1
+ }
+
+ override fun writeEvent(tag: Int, value: Float): Int {
+ _events.add(Event(tag, value))
+ return 1
+ }
+
+ override fun writeEvent(tag: Int, value: String): Int {
+ _events.add(Event(tag, value))
+ return 1
+ }
+
+ override fun writeEvent(tag: Int, vararg values: Any): Int {
+ _events.add(Event(tag, values))
+ return 1
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/FakeAnimationUtilDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/FakeAnimationUtilDataLayerModule.kt
new file mode 100644
index 000000000000..f7830d9655a7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/FakeAnimationUtilDataLayerModule.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.animation.data
+
+import com.android.systemui.util.animation.data.repository.FakeAnimationStatusRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeAnimationStatusRepositoryModule::class])
+object FakeAnimationUtilDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/repository/FakeAnimationStatusRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/repository/FakeAnimationStatusRepository.kt
new file mode 100644
index 000000000000..ca6628b7f357
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/animation/data/repository/FakeAnimationStatusRepository.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.util.animation.data.repository
+
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeAnimationStatusRepository @Inject constructor() : AnimationStatusRepository {
+
+ // Replay 1 element as real repository always emits current status as a first element
+ private val animationsEnabled: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
+
+ override fun areAnimationsEnabled(): Flow<Boolean> = animationsEnabled
+
+ fun onAnimationStatusChanged(enabled: Boolean) {
+ animationsEnabled.tryEmit(enabled)
+ }
+}
+
+@Module
+interface FakeAnimationStatusRepositoryModule {
+ @Binds fun bindFake(fake: FakeAnimationStatusRepository): AnimationStatusRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index db5eaffee76b..beabaf5f5954 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -38,7 +38,9 @@ public class FakeGlobalSettings implements GlobalSettings {
@Override
public ContentResolver getContentResolver() {
- return null;
+ throw new UnsupportedOperationException(
+ "GlobalSettings.getContentResolver is not implemented, but you may find "
+ + "GlobalSettings.registerContentObserver helpful instead.");
}
@Override
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index bdf1aff7f985..d45281005309 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -24,6 +24,9 @@ public class FakeKeyguardStateController implements KeyguardStateController {
private final BaseLeakChecker<Callback> mCallbackController;
+ private boolean mIsShowing = false;
+ private boolean mIsOccluded = false;
+
public FakeKeyguardStateController(LeakCheck test) {
mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
}
@@ -45,7 +48,11 @@ public class FakeKeyguardStateController implements KeyguardStateController {
@Override
public boolean isShowing() {
- return false;
+ return mIsShowing;
+ }
+
+ public void setShowing(boolean showing) {
+ mIsShowing = showing;
}
@Override
@@ -60,7 +67,11 @@ public class FakeKeyguardStateController implements KeyguardStateController {
@Override
public boolean isOccluded() {
- return false;
+ return mIsOccluded;
+ }
+
+ public void setOccluded(boolean occluded) {
+ mIsOccluded = occluded;
}
@Override
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 4c224fb33b26..fb521e11c083 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -359,7 +359,7 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_doesApplyLater() throws IOException {
+ public void testUpdateWallpaperComponent_systemAndLock() throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
/* which */ FLAG_LOCK | FLAG_SYSTEM);
@@ -377,7 +377,7 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen()
+ public void testUpdateWallpaperComponent_systemOnly()
throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
@@ -617,7 +617,7 @@ public class WallpaperBackupAgentTest {
mWallpaperBackupAgent.onRestoreFinished();
- // wallpaper will be applied to home & lock screen, a success for both screens in expected
+ // wallpaper will be applied to home & lock screen, a success for both screens is expected
DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
assertThat(result).isNotNull();
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 5c9bf1828347..3f46ab859bf9 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -16,15 +16,6 @@ filegroup {
visibility: ["//visibility:public"],
}
-// File that contains the standard command line arguments to hoststubgen.
-filegroup {
- name: "ravenwood-standard-options",
- srcs: [
- "ravenwood-standard-options.txt",
- ],
- visibility: ["//visibility:public"],
-}
-
java_library {
name: "ravenwood-annotations-lib",
srcs: [":ravenwood-annotations"],
@@ -34,12 +25,38 @@ java_library {
}
java_library {
+ name: "ravenwood-junit-impl",
+ srcs: [
+ "junit-src/**/*.java",
+ "junit-impl-src/**/*.java",
+ ],
+ libs: [
+ "framework-minus-apex.ravenwood",
+ "junit",
+ ],
+ visibility: ["//frameworks/base"],
+}
+
+// Carefully compiles against only test_current to support tests that
+// want to verify they're unbundled. The "impl" library above is what
+// ships inside the Ravenwood environment to actually drive any API
+// access to implementation details.
+java_library {
name: "ravenwood-junit",
- srcs: ["junit-src/**/*.java"],
+ srcs: [
+ "junit-src/**/*.java",
+ "junit-stub-src/**/*.java",
+ ],
+ sdk_version: "test_current",
libs: [
"junit",
],
- sdk_version: "core_current",
- host_supported: true,
visibility: ["//visibility:public"],
}
+
+java_host_for_device {
+ name: "core-xml-for-device",
+ libs: [
+ "core-xml-for-host",
+ ],
+}
diff --git a/ravenwood/README-ravenwood+mockito.md b/ravenwood/README-ravenwood+mockito.md
new file mode 100644
index 000000000000..6adb61441bb1
--- /dev/null
+++ b/ravenwood/README-ravenwood+mockito.md
@@ -0,0 +1,24 @@
+# Ravenwood and Mockito
+
+Last update: 2023-11-13
+
+- As of 2023-11-13, `external/mockito` is based on version 2.x.
+- Mockito didn't support static mocking before 3.4.0.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+- Latest Mockito is 5.*. According to https://github.com/mockito/mockito:
+ `Mockito 3 does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. Mockito 4 removes deprecated API. Mockito 5 switches the default mockmaker to mockito-inline, and now requires Java 11.`
+
+- Mockito now supports Android natively.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1
+ - But it's unclear at this point to omakoto@ how the `mockito-android` module is built.
+
+- Potential plan:
+ - Ideal option:
+ - If we can update `external/mockito`, that'd be great, but it may not work because
+ Mockito has removed the deprecated APIs.
+ - Second option:
+ - Import the latest mockito as `external/mockito-new`, and require ravenwood
+ to use this one.
+ - The latest mockito needs be exposed to all of 1) device tests, 2) host tests, and 3) ravenwood tests.
+ - This probably will require the latest `bytebuddy` and `objenesis`. \ No newline at end of file
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
new file mode 100644
index 000000000000..72eb665bee65
--- /dev/null
+++ b/ravenwood/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ // Let's only run this one as a smoke test.
+ // TODO: Enable it once the infra knows how to run it.
+ // { "name": "CtsUtilTestCasesRavenwood" }
+ ]
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
index 76964a72dd3e..7dc197e6bdfd 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
index ddf65dc2c5ac..f02f06c056bd 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,7 +31,7 @@ import java.lang.annotation.Target;
*
* @hide
*/
-@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Target({FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface RavenwoodKeep {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java
new file mode 100644
index 000000000000..784727410188
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepPartialClass.java
@@ -0,0 +1,34 @@
+/*
+ * 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.ravenwood.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ *
+ * @hide
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeepPartialClass {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java
new file mode 100644
index 000000000000..eeebee985e4a
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepStaticInitializer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.ravenwood.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+@Target(TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeepStaticInitializer {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
index d7ef7f55921b..d2c77c1b8566 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
@@ -35,5 +35,5 @@ import java.lang.annotation.Target;
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodWholeClassKeep {
+public @interface RavenwoodKeepWholeClass {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
index 8cdc1ff91081..4b9cf85e16fa 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
index 759c918c4a66..6727327c99be 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
index 5a0a8f4f5aae..a920f63152fb 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.METHOD;
@@ -31,8 +31,5 @@ import java.lang.annotation.Target;
*/
@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodSubstitute {
- // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
- // value with ASM doesn't seem trivial. (? not sure.)
- String suffix();
+public @interface RavenwoodReplace {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
index de3dd0465c59..a234a9b6fc7c 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
diff --git a/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 48c0a2de8f86..48e93280f73f 100644
--- a/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -17,12 +17,14 @@ class android.util.MapCollections stubclass
class android.util.Log stubclass
class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host
class android.util.LogPrinter stubclass
+class android.util.LocalLog stubclass
# String Manipulation
class android.util.Printer stubclass
class android.util.PrintStreamPrinter stubclass
class android.util.PrintWriterPrinter stubclass
class android.util.StringBuilderPrinter stubclass
+class android.util.IndentingPrintWriter stubclass
# Properties
class android.util.Property stubclass
@@ -76,21 +78,14 @@ class android.util.Patterns stubclass
class android.util.UtilConfig stubclass
# Internals
-class com.android.internal.util.ArrayUtils stubclass
- method newUnpaddedByteArray (I)[B @newUnpaddedByteArray$ravenwood
- method newUnpaddedCharArray (I)[C @newUnpaddedCharArray$ravenwood
- method newUnpaddedIntArray (I)[I @newUnpaddedIntArray$ravenwood
- method newUnpaddedBooleanArray (I)[Z @newUnpaddedBooleanArray$ravenwood
- method newUnpaddedLongArray (I)[J @newUnpaddedLongArray$ravenwood
- method newUnpaddedFloatArray (I)[F @newUnpaddedFloatArray$ravenwood
- method newUnpaddedObjectArray (I)[Ljava/lang/Object; @newUnpaddedObjectArray$ravenwood
- method newUnpaddedArray (Ljava/lang/Class;I)[Ljava/lang/Object; @newUnpaddedArray$ravenwood
-
+class com.android.internal.util.FastPrintWriter stubclass
class com.android.internal.util.GrowingArrayUtils stubclass
class com.android.internal.util.LineBreakBufferedWriter stubclass
class com.android.internal.util.Preconditions stubclass
class com.android.internal.util.StringPool stubclass
+class com.android.internal.os.SomeArgs stubclass
+
# Parcel
class android.os.Parcel stubclass
method writeException (Ljava/lang/Exception;)V @writeException$ravenwood
@@ -102,14 +97,38 @@ class android.os.ParcelFormatException stubclass
class android.os.BadParcelableException stubclass
class android.os.BadTypeParcelableException stubclass
-# Binder: just enough to construct, no further functionality
-class android.os.Binder stub
- method <init> ()V stub
- method <init> (Ljava/lang/String;)V stub
- method isDirectlyHandlingTransaction ()Z stub
- method isDirectlyHandlingTransactionNative ()Z @isDirectlyHandlingTransactionNative$ravenwood
- method getNativeBBinderHolder ()J @getNativeBBinderHolder$ravenwood
+# Binder
+class android.os.DeadObjectException stubclass
+class android.os.DeadSystemException stubclass
+class android.os.RemoteException stubclass
+class android.os.TransactionTooLargeException stubclass
# Containers
class android.os.BaseBundle stubclass
class android.os.Bundle stubclass
+class android.os.PersistableBundle stubclass
+
+# Misc
+class android.os.PatternMatcher stubclass
+class android.os.ParcelUuid stubclass
+
+# XML
+class com.android.internal.util.XmlPullParserWrapper stubclass
+class com.android.internal.util.XmlSerializerWrapper stubclass
+class com.android.internal.util.XmlUtils stubclass
+
+class com.android.modules.utils.BinaryXmlPullParser stubclass
+class com.android.modules.utils.BinaryXmlSerializer stubclass
+class com.android.modules.utils.FastDataInput stubclass
+class com.android.modules.utils.FastDataOutput stubclass
+class com.android.modules.utils.ModifiedUtf8 stubclass
+class com.android.modules.utils.TypedXmlPullParser stubclass
+class com.android.modules.utils.TypedXmlSerializer stubclass
+
+# Uri
+class android.net.Uri stubclass
+class android.net.UriCodec stubclass
+
+# Context: just enough to support wrapper, no further functionality
+class android.content.Context stub
+ method <init> ()V stub
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
new file mode 100644
index 000000000000..1caef26d50eb
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -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 android.platform.test.ravenwood;
+
+public class RavenwoodRuleImpl {
+ public static void init(RavenwoodRule rule) {
+ android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
+ android.os.Binder.init$ravenwood();
+ }
+
+ public static void reset(RavenwoodRule rule) {
+ android.os.Process.reset$ravenwood();
+ android.os.Binder.reset$ravenwood();
+ }
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
index 0aac084dd4ce..edb0442e7b29 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
@@ -22,12 +22,30 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
+ * Test methods marked with this annotation are quietly ignored when running under a Ravenwood test
+ * environment. The test continues to execute normally under all other non-Ravenwood test
+ * environments.
+ *
+ * This annotation only takes effect when the containing class has a {@code
+ * RavenwoodRule} configured. Ignoring is accomplished by throwing an {@code org.junit
+ * .AssumptionViolatedException} which test infrastructure treats as being ignored.
+ *
+ * Developers are encouraged to use either the {@code blockedBy} and/or {@code reason} arguments
+ * to document why a test is being ignored, to aid in future audits of tests that are candidates
+ * to be enabled.
*
* @hide
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreUnderRavenwood {
+ /**
+ * One or more classes that aren't yet supported by Ravenwood, which this test depends on.
+ */
+ Class<?>[] blockedBy() default {};
+
+ /**
+ * General free-form description of why this test is being ignored.
+ */
+ String reason() default "";
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a6b3f668efa6..79f9e58aaa1a 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -23,6 +23,8 @@ import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
* QUESTIONS ABOUT IT.
@@ -30,6 +32,54 @@ import org.junit.runners.model.Statement;
* @hide
*/
public class RavenwoodRule implements TestRule {
+ private static AtomicInteger sNextPid = new AtomicInteger(100);
+
+ private static final int SYSTEM_UID = 1000;
+ private static final int NOBODY_UID = 9999;
+ private static final int FIRST_APPLICATION_UID = 10000;
+
+ /**
+ * Unless the test author requests differently, run as "nobody", and give each collection of
+ * tests its own unique PID.
+ */
+ int mUid = NOBODY_UID;
+ int mPid = sNextPid.getAndIncrement();
+
+ public RavenwoodRule() {
+ }
+
+ public static class Builder {
+ private RavenwoodRule mRule = new RavenwoodRule();
+
+ public Builder() {
+ }
+
+ /**
+ * Configure the identity of this process to be the system UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessSystem() {
+ mRule.mUid = SYSTEM_UID;
+ return this;
+ }
+
+ /**
+ * Configure the identity of this process to be an app UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessApp() {
+ mRule.mUid = FIRST_APPLICATION_UID;
+ return this;
+ }
+
+ public RavenwoodRule build() {
+ return mRule;
+ }
+ }
+
+ /**
+ * Return if the current process is running under a Ravenwood test environment.
+ */
public boolean isUnderRavenwood() {
// TODO: give ourselves a better environment signal
return System.getProperty("java.class.path").contains("ravenwood");
@@ -40,10 +90,20 @@ public class RavenwoodRule implements TestRule {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ final boolean isUnderRavenwood = isUnderRavenwood();
if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
- Assume.assumeFalse(isUnderRavenwood());
+ Assume.assumeFalse(isUnderRavenwood);
+ }
+ if (isUnderRavenwood) {
+ RavenwoodRuleImpl.init(RavenwoodRule.this);
+ }
+ try {
+ base.evaluate();
+ } finally {
+ if (isUnderRavenwood) {
+ RavenwoodRuleImpl.reset(RavenwoodRule.this);
+ }
}
- base.evaluate();
}
};
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
new file mode 100644
index 000000000000..ecaff8084888
--- /dev/null
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -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 android.platform.test.ravenwood;
+
+public class RavenwoodRuleImpl {
+ public static void init(RavenwoodRule rule) {
+ // Must be provided by impl to reference runtime internals
+ throw new UnsupportedOperationException();
+ }
+
+ public static void reset(RavenwoodRule rule) {
+ // Must be provided by impl to reference runtime internals
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/mockito/Android.bp
new file mode 100644
index 000000000000..4135022d2bc9
--- /dev/null
+++ b/ravenwood/mockito/Android.bp
@@ -0,0 +1,72 @@
+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"],
+}
+
+// Ravenwood tests run on the hostside, so we need mockit of the host variant.
+// But we need to use it in modules of the android variant, so we "wash" the variant with it.
+java_host_for_device {
+ name: "mockito_ravenwood",
+ libs: [
+ "mockito",
+ "objenesis",
+ ],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodMockitoTest",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "mockito_ravenwood",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ auto_gen_config: true,
+}
+
+android_test {
+ name: "RavenwoodMockitoTest_device",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "androidx.test.rules",
+
+ "ravenwood-junit",
+
+ "mockito-target-extended-minus-junit4",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ // Required by mockito
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/mockito/AndroidManifest.xml
new file mode 100644
index 000000000000..15f0a2934b5f
--- /dev/null
+++ b/ravenwood/mockito/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.ravenwood.mockitotest">
+
+ <application android:debuggable="true" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.ravenwood.mockitotest"
+ />
+</manifest>
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/mockito/AndroidTest.xml
new file mode 100644
index 000000000000..96bc2752fe95
--- /dev/null
+++ b/ravenwood/mockito/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="RavenwoodMockitoTest_device.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.ravenwood.mockitotest" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
new file mode 100644
index 000000000000..36fa3dd94e29
--- /dev/null
+++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.ravenwood.mockito;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public class RavenwoodMockitoTest {
+ @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+
+// Use this to mock static methods, which isn't supported by mockito 2.
+// Mockito supports static mocking since 3.4.0:
+// See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+// private MockitoSession mMockingSession;
+//
+// @Before
+// public void setUp() {
+// mMockingSession = mockitoSession()
+// .strictness(Strictness.LENIENT)
+// .mockStatic(RavenwoodMockitoTest.class)
+// .startMocking();
+// }
+//
+// @After
+// public void tearDown() {
+// if (mMockingSession != null) {
+// mMockingSession.finishMocking();
+// }
+// }
+
+ @Test
+ public void testMockJdkClass() {
+ Process object = mock(Process.class);
+
+ when(object.exitValue()).thenReturn(42);
+
+ assertThat(object.exitValue()).isEqualTo(42);
+ }
+
+ /*
+ - Intent can't be mocked because of the dependency to `org.xmlpull.v1.XmlPullParser`.
+ (The error says "Mockito can only mock non-private & non-final classes", but that's likely a
+ red-herring.)
+
+STACKTRACE:
+org.mockito.exceptions.base.MockitoException:
+Mockito cannot mock this class: class android.content.Intent.
+
+ :
+
+Underlying exception : java.lang.IllegalArgumentException: Could not create type
+ at com.android.ravenwood.mockito.RavenwoodMockitoTest.testMockAndroidClass1
+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+
+ :
+
+Caused by: java.lang.ClassNotFoundException: org.xmlpull.v1.XmlPullParser
+ at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
+ at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
+ at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
+ ... 54 more
+ */
+ @Test
+ @IgnoreUnderRavenwood
+ public void testMockAndroidClass1() {
+ Intent object = mock(Intent.class);
+
+ when(object.getAction()).thenReturn("ACTION_RAVENWOOD");
+
+ assertThat(object.getAction()).isEqualTo("ACTION_RAVENWOOD");
+ }
+
+ @Test
+ public void testMockAndroidClass2() {
+ Context object = mock(Context.class);
+
+ when(object.getPackageName()).thenReturn("android");
+
+ assertThat(object.getPackageName()).isEqualTo("android");
+ }
+}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
new file mode 100644
index 000000000000..a791f682fecf
--- /dev/null
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -0,0 +1,46 @@
+# Only classes listed here can use the Ravenwood annotations.
+
+com.android.internal.util.ArrayUtils
+
+android.util.DataUnit
+android.util.EventLog
+android.util.IntArray
+android.util.LongArray
+android.util.Slog
+android.util.TimeUtils
+android.util.Xml
+
+android.os.Binder
+android.os.Binder$IdentitySupplier
+android.os.IBinder
+android.os.Process
+android.os.SystemClock
+android.os.UserHandle
+
+android.content.ClipData
+android.content.ClipData$Item
+android.content.ClipDescription
+android.content.ComponentName
+android.content.ContentUris
+android.content.ContentValues
+android.content.Intent
+android.content.IntentFilter
+android.content.UriMatcher
+
+android.database.AbstractCursor
+android.database.CharArrayBuffer
+android.database.ContentObservable
+android.database.ContentObserver
+android.database.Cursor
+android.database.CursorIndexOutOfBoundsException
+android.database.CursorJoiner
+android.database.CursorWrapper
+android.database.DataSetObservable
+android.database.DataSetObserver
+android.database.MatrixCursor
+android.database.MatrixCursor$RowBuilder
+android.database.MergeCursor
+android.database.Observable
+
+android.text.TextUtils
+android.text.TextUtils$SimpleStringSplitter
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index 6e1384f368b8..f842f33bc95b 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -16,22 +16,28 @@
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
--keep-annotation
- android.ravenwood.annotations.RavenwoodKeep
+ android.ravenwood.annotation.RavenwoodKeep
+
+--keep-annotation
+ android.ravenwood.annotation.RavenwoodKeepPartialClass
--keep-class-annotation
- android.ravenwood.annotations.RavenwoodWholeClassKeep
+ android.ravenwood.annotation.RavenwoodKeepWholeClass
--throw-annotation
- android.ravenwood.annotations.RavenwoodThrow
+ android.ravenwood.annotation.RavenwoodThrow
--remove-annotation
- android.ravenwood.annotations.RavenwoodRemove
+ android.ravenwood.annotation.RavenwoodRemove
--substitute-annotation
- android.ravenwood.annotations.RavenwoodSubstitute
+ android.ravenwood.annotation.RavenwoodReplace
--native-substitute-annotation
- android.ravenwood.annotations.RavenwoodNativeSubstitutionClass
+ android.ravenwood.annotation.RavenwoodNativeSubstitutionClass
--class-load-hook-annotation
- android.ravenwood.annotations.RavenwoodClassLoadHook
+ android.ravenwood.annotation.RavenwoodClassLoadHook
+
+--keep-static-initializer-annotation
+ android.ravenwood.annotation.RavenwoodKeepStaticInitializer
diff --git a/services/Android.bp b/services/Android.bp
index f1534b461607..02a7a78653aa 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -63,6 +63,7 @@ system_optimized_java_defaults {
optimize: false,
shrink: true,
ignore_warnings: false,
+ proguard_compatibility: false,
proguard_flags_files: [
"proguard.flags",
// Ensure classes referenced in the framework-res manifest
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 6d82b749fda3..041cd75f82e7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -26,7 +26,6 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILIT
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
@@ -35,6 +34,8 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCE
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+import static com.android.window.flags.Flags.removeCaptureDisplay;
+
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -69,7 +70,6 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.Trace;
import android.provider.Settings;
import android.util.Pair;
import android.util.Slog;
@@ -93,7 +93,6 @@ import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
-import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -101,7 +100,6 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -1444,42 +1442,86 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
return;
}
-
final long identity = Binder.clearCallingIdentity();
- try {
- mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
- final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
- .getService(DisplayManagerInternal.class).userScreenshot(displayId);
- if (screenshotBuffer != null) {
- sendScreenshotSuccess(screenshotBuffer, callback);
- } else {
- sendScreenshotFailure(
- AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
- }
- }, null).recycleOnUse());
- } finally {
- Binder.restoreCallingIdentity(identity);
+ if (removeCaptureDisplay()) {
+ try {
+ ScreenCapture.ScreenCaptureListener screenCaptureListener = new
+ ScreenCapture.ScreenCaptureListener(
+ (screenshotBuffer, result) -> {
+ if (screenshotBuffer != null && result == 0) {
+ sendScreenshotSuccess(screenshotBuffer, callback);
+ } else {
+ sendScreenshotFailure(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+ callback);
+ }
+ }
+ );
+ mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+ } catch (Exception e) {
+ sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+ callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ try {
+ mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+ final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
+ .getService(DisplayManagerInternal.class).userScreenshot(displayId);
+ if (screenshotBuffer != null) {
+ sendScreenshotSuccess(screenshotBuffer, callback);
+ } else {
+ sendScreenshotFailure(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+ callback);
+ }
+ }, null).recycleOnUse());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer,
RemoteCallback callback) {
- final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- final ParcelableColorSpace colorSpace =
- new ParcelableColorSpace(screenshotBuffer.getColorSpace());
+ if (removeCaptureDisplay()) {
+ mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+ final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ final ParcelableColorSpace colorSpace =
+ new ParcelableColorSpace(screenshotBuffer.getColorSpace());
+
+ final Bundle payload = new Bundle();
+ payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
+ AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+ hardwareBuffer);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+ payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
+ SystemClock.uptimeMillis());
+
+ // Send back the result.
+ callback.sendResult(payload);
+ hardwareBuffer.close();
+ }, null).recycleOnUse());
+ } else {
+ final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ final ParcelableColorSpace colorSpace =
+ new ParcelableColorSpace(screenshotBuffer.getColorSpace());
- final Bundle payload = new Bundle();
- payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
- AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
- payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
- hardwareBuffer);
- payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
- payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
- SystemClock.uptimeMillis());
+ final Bundle payload = new Bundle();
+ payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
+ AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+ hardwareBuffer);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+ payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
+ SystemClock.uptimeMillis());
- // Send back the result.
- callback.sendResult(payload);
- hardwareBuffer.close();
+ // Send back the result.
+ callback.sendResult(payload);
+ hardwareBuffer.close();
+ }
}
private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
@@ -1993,20 +2035,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void createImeSessionInternal() {
- final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
- if (listener != null) {
- try {
- if (svcClientTracingEnabled()) {
- logTraceSvcClient("createImeSession", "");
- }
- AccessibilityCallback callback = new AccessibilityCallback();
- listener.createImeSession(callback);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG,
- "Error requesting IME session from " + mService, re);
- }
- }
+ protected void createImeSessionInternal() {
}
private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
@@ -2658,21 +2687,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private static final class AccessibilityCallback
- extends IAccessibilityInputMethodSessionCallback.Stub {
- @Override
- public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AACS.sessionCreated");
- final long ident = Binder.clearCallingIdentity();
- try {
- InputMethodManagerInternal.get().onSessionForAccessibilityCreated(id, session);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
@Override
public void attachAccessibilityOverlayToDisplay(
int interactionId,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index bb50a991d9c1..b5e8c849517b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -5378,19 +5378,37 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void requestImeLocked(AbstractAccessibilityServiceConnection connection) {
+ if (!(connection instanceof AccessibilityServiceConnection)
+ || (connection instanceof ProxyAccessibilityServiceConnection)) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "The connection should be a real connection but was "
+ + connection);
+ }
+ return;
+ }
+ AccessibilityServiceConnection realConnection = (AccessibilityServiceConnection) connection;
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::createSessionForConnection, this, connection));
+ AccessibilityManagerService::createSessionForConnection, this, realConnection));
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::bindAndStartInputForConnection, this, connection));
+ AccessibilityManagerService::bindAndStartInputForConnection, this, realConnection));
}
@Override
public void unbindImeLocked(AbstractAccessibilityServiceConnection connection) {
+ if (!(connection instanceof AccessibilityServiceConnection)
+ || (connection instanceof ProxyAccessibilityServiceConnection)) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "The connection should be a real connection but was "
+ + connection);
+ }
+ return;
+ }
+ AccessibilityServiceConnection realConnection = (AccessibilityServiceConnection) connection;
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::unbindInputForConnection, this, connection));
+ AccessibilityManagerService::unbindInputForConnection, this, realConnection));
}
- private void createSessionForConnection(AbstractAccessibilityServiceConnection connection) {
+ private void createSessionForConnection(AccessibilityServiceConnection connection) {
synchronized (mLock) {
if (mInputSessionRequested) {
connection.createImeSessionLocked();
@@ -5398,7 +5416,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void bindAndStartInputForConnection(AbstractAccessibilityServiceConnection connection) {
+ private void bindAndStartInputForConnection(AccessibilityServiceConnection connection) {
synchronized (mLock) {
if (mInputBound) {
connection.bindInputLocked();
@@ -5407,8 +5425,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void unbindInputForConnection(AbstractAccessibilityServiceConnection connection) {
- InputMethodManagerInternal.get().unbindAccessibilityFromCurrentClient(connection.mId);
+ private void unbindInputForConnection(AccessibilityServiceConnection connection) {
+ InputMethodManagerInternal.get()
+ .unbindAccessibilityFromCurrentClient(connection.mId, connection.mUserId);
synchronized (mLock) {
connection.unbindInputLocked();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 7a2a60263d38..40ca694dddd8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -18,6 +18,7 @@ package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -27,6 +28,8 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.TouchInteractionController;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -38,12 +41,15 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
import android.view.MotionEvent;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -70,13 +76,38 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
Having the reference be null when being called is a very bad sign, but we check the condition.
*/
final WeakReference<AccessibilityUserState> mUserStateWeakReference;
+ @UserIdInt
+ final int mUserId;
final Intent mIntent;
final ActivityTaskManagerInternal mActivityTaskManagerService;
private final Handler mMainHandler;
- AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
- ComponentName componentName,
+ private static final class AccessibilityInputMethodSessionCallback
+ extends IAccessibilityInputMethodSessionCallback.Stub {
+ @UserIdInt
+ private final int mUserId;
+
+ AccessibilityInputMethodSessionCallback(@UserIdInt int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ASC.sessionCreated");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ InputMethodManagerInternal.get()
+ .onSessionForAccessibilityCreated(id, session, mUserId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ AccessibilityServiceConnection(@Nullable AccessibilityUserState userState,
+ Context context, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
@@ -86,6 +117,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerfomer,
awm);
mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
+ // the user ID doesn't matter when userState is null, because it is null only when this is a
+ // ProxyAccessibilityServiceConnection, for which it never creates an IME session and uses
+ // the user ID.
+ mUserId = userState == null ? UserHandle.USER_NULL : userState.mUserId;
mIntent = new Intent().setComponent(mComponentName);
mMainHandler = mainHandler;
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -557,6 +592,24 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
return mRequestImeApis;
}
+ @Override
+ protected void createImeSessionInternal() {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("createImeSession", "");
+ }
+ AccessibilityInputMethodSessionCallback
+ callback = new AccessibilityInputMethodSessionCallback(mUserId);
+ listener.createImeSession(callback);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error requesting IME session from " + mService, re);
+ }
+ }
+ }
+
private void notifyMotionEventInternal(MotionEvent event) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 45ca7260bbe7..d31b1efafcc4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -69,6 +69,7 @@ import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.gestures.GestureUtils;
/**
@@ -261,8 +262,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
mDelegatingState = new DelegatingState();
- mDetectingState = new DetectingState(context);
- mViewportDraggingState = new ViewportDraggingState();
+ mDetectingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ ? new DetectingStateWithMultiFinger(context)
+ : new DetectingState(context);
+ mViewportDraggingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ ? new ViewportDraggingStateWithMultiFinger()
+ : new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
mSinglePanningState = new SinglePanningState(context);
mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
@@ -634,6 +639,62 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+ final class ViewportDraggingStateWithMultiFinger extends ViewportDraggingState {
+ // LINT.IfChange(viewport_dragging_state_with_multi_finger)
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
+ throws GestureException {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_POINTER_DOWN: {
+ clearAndTransitToPanningScalingState();
+ }
+ break;
+ case ACTION_MOVE: {
+ if (event.getPointerCount() > 2) {
+ throw new GestureException("Should have one pointer down.");
+ }
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, eventX, eventY)) {
+ mFullScreenMagnificationController.setCenter(mDisplayId, eventX, eventY,
+ /* animate */ mLastMoveOutsideMagnifiedRegion,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mLastMoveOutsideMagnifiedRegion = false;
+ } else {
+ mLastMoveOutsideMagnifiedRegion = true;
+ }
+ }
+ break;
+
+ case ACTION_UP:
+ case ACTION_CANCEL: {
+ // If mScaleToRecoverAfterDraggingEnd >= 1.0, the dragging state is triggered
+ // by zoom in temporary, and the magnifier needs to recover to original scale
+ // after exiting dragging state.
+ // Otherwise, the magnifier should be disabled.
+ if (mScaleToRecoverAfterDraggingEnd >= 1.0f) {
+ zoomToScale(mScaleToRecoverAfterDraggingEnd, event.getX(),
+ event.getY());
+ } else {
+ zoomOff();
+ }
+ clear();
+ mScaleToRecoverAfterDraggingEnd = Float.NaN;
+ transitionTo(mDetectingState);
+ }
+ break;
+
+ case ACTION_DOWN: {
+ throw new GestureException(
+ "Unexpected event type: " + MotionEvent.actionToString(action));
+ }
+ }
+ }
+ // LINT.ThenChange(:viewport_dragging_state)
+ }
+
/**
* This class handles motion events when the event dispatcher has
* determined that the user is performing a single-finger drag of the
@@ -643,17 +704,18 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
* of the finger, and any part of the screen is reachable without lifting the finger.
* This makes it the preferable mode for tasks like reading text spanning full screen width.
*/
- final class ViewportDraggingState implements State {
+ class ViewportDraggingState implements State {
/**
* The cached scale for recovering after dragging ends.
* If the scale >= 1.0, the magnifier needs to recover to scale.
* Otherwise, the magnifier should be disabled.
*/
- @VisibleForTesting float mScaleToRecoverAfterDraggingEnd = Float.NaN;
+ @VisibleForTesting protected float mScaleToRecoverAfterDraggingEnd = Float.NaN;
- private boolean mLastMoveOutsideMagnifiedRegion;
+ protected boolean mLastMoveOutsideMagnifiedRegion;
+ // LINT.IfChange(viewport_dragging_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
throws GestureException {
@@ -706,6 +768,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
}
+ // LINT.ThenChange(:viewport_dragging_state_with_multi_finger)
private boolean isAlwaysOnMagnificationEnabled() {
return mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled();
@@ -732,7 +795,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
? mFullScreenMagnificationController.getScale(mDisplayId) : Float.NaN;
}
- private void clearAndTransitToPanningScalingState() {
+ protected void clearAndTransitToPanningScalingState() {
final float scaleToRecovery = mScaleToRecoverAfterDraggingEnd;
clear();
mScaleToRecoverAfterDraggingEnd = scaleToRecovery;
@@ -791,36 +854,220 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+ final class DetectingStateWithMultiFinger extends DetectingState {
+ // A flag set to true when two fingers have touched down.
+ // Used to indicate what next finger action should be.
+ private boolean mIsTwoFingerCountReached = false;
+ // A tap counts when two fingers are down and up once.
+ private int mCompletedTapCount = 0;
+ DetectingStateWithMultiFinger(Context context) {
+ super(context);
+ }
+
+ // LINT.IfChange(detecting_state_with_multi_finger)
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cacheDelayedMotionEvent(event, rawEvent, policyFlags);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mLastDetectingDownEventTime = event.getDownTime();
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, event.getX(), event.getY())) {
+
+ transitionToDelegatingStateAndClear();
+
+ } else if (isMultiTapTriggered(2 /* taps */)) {
+
+ // 3tap and hold
+ afterLongTapTimeoutTransitionToDraggingState(event);
+
+ } else if (isTapOutOfDistanceSlop()) {
+
+ transitionToDelegatingStateAndClear();
+
+ } else if (mDetectSingleFingerTripleTap
+ || mDetectTwoFingerTripleTap
+ // If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
+ // to ensure reachability of
+ // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
+ || isActivated()) {
+
+ afterMultiTapTimeoutTransitionToDelegatingState();
+
+ } else {
+
+ // Delegate pending events without delay
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_POINTER_DOWN: {
+ mIsTwoFingerCountReached = mDetectTwoFingerTripleTap
+ && event.getPointerCount() == 2;
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+ if (isActivated() && event.getPointerCount() == 2) {
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
+ } else if (mIsTwoFingerCountReached) {
+ // Placing two-finger triple-taps behind isActivated to avoid
+ // blocking panning scaling state
+ if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)) {
+ // 3tap and hold
+ afterLongTapTimeoutTransitionToDraggingState(event);
+ } else {
+ afterMultiTapTimeoutTransitionToDelegatingState();
+ }
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_POINTER_UP: {
+ // If it is a two-finger gesture, do not transition to the delegating state
+ // to ensure the reachability of
+ // the two-finger triple tap (triggerable with ACTION_MOVE and ACTION_UP)
+ if (!mIsTwoFingerCountReached) {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_MOVE: {
+ if (isFingerDown()
+ && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
+ // Swipe detected - transition immediately
+
+ // For convenience, viewport dragging takes precedence
+ // over insta-delegating on 3tap&swipe
+ // (which is a rare combo to be used aside from magnification)
+ if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
+ transitionToViewportDraggingStateAndClear(event);
+ } else if (isActivated() && event.getPointerCount() == 2) {
+ if (mIsSinglePanningEnabled
+ && overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ }
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)
+ && event.getPointerCount() == 2) {
+ // Placing two-finger triple-taps behind isActivated to avoid
+ // blocking panning scaling state
+ transitionToViewportDraggingStateAndClear(event);
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1) {
+ if (overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ }
+ transitToSinglePanningStateAndClear();
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
+ && distanceClosestPointerToPoint(
+ mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
+ //Second pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_UP: {
+
+ mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, event.getX(), event.getY())) {
+ transitionToDelegatingStateAndClear();
+
+ } else if (isMultiTapTriggered(3 /* taps */)) {
+ onTripleTap(/* up */ event);
+
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ onTripleTap(event);
+
+ } else if (
+ // Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
+ isFingerDown()
+ //TODO long tap should never happen here
+ && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
+ || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))
+ // If it is a two-finger but not reach 3 tap, do not transition to the
+ // delegating state to ensure the reachability of the triple tap
+ && mCompletedTapCount == 0) {
+ transitionToDelegatingStateAndClear();
+
+ }
+ }
+ break;
+ }
+ }
+ // LINT.ThenChange(:detecting_state)
+
+ @Override
+ public void clear() {
+ mCompletedTapCount = 0;
+ setShortcutTriggered(false);
+ removePendingDelayedMessages();
+ clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
+ mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
+ }
+
+ private boolean isMultiFingerMultiTapTriggered(int targetTapCount, MotionEvent event) {
+ if (event.getActionMasked() == ACTION_UP && mIsTwoFingerCountReached) {
+ mCompletedTapCount++;
+ mIsTwoFingerCountReached = false;
+ }
+ return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
+ }
+
+ void transitionToDelegatingStateAndClear() {
+ mCompletedTapCount = 0;
+ transitionTo(mDelegatingState);
+ sendDelayedMotionEvents();
+ removePendingDelayedMessages();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
+ mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
+ }
+ }
+
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
*/
- final class DetectingState implements State, Handler.Callback {
+ class DetectingState implements State, Handler.Callback {
- private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
- private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
- private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
+ protected static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
+ protected static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
+ protected static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
final int mLongTapMinDelay;
final int mSwipeMinDistance;
final int mMultiTapMaxDelay;
final int mMultiTapMaxDistance;
- private MotionEventInfo mDelayedEventQueue;
- MotionEvent mLastDown;
- private MotionEvent mPreLastDown;
- private MotionEvent mLastUp;
- private MotionEvent mPreLastUp;
- private PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected MotionEventInfo mDelayedEventQueue;
+ protected MotionEvent mLastDown;
+ protected MotionEvent mPreLastDown;
+ protected MotionEvent mLastUp;
+ protected MotionEvent mPreLastUp;
- private long mLastDetectingDownEventTime;
+ protected PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected long mLastDetectingDownEventTime;
@VisibleForTesting boolean mShortcutTriggered;
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
- private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
-
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -855,6 +1102,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return true;
}
+ // LINT.IfChange(detecting_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
@@ -969,23 +1217,24 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
break;
}
}
+ // LINT.ThenChange(:detecting_state_with_multi_finger)
- private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+ protected void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
final int index = event.getActionIndex();
pointerDownLocation.set(event.getX(index), event.getY(index));
}
- private boolean pointerDownValid(PointF pointerDownLocation) {
+ protected boolean pointerDownValid(PointF pointerDownLocation) {
return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
pointerDownLocation.y));
}
- private void transitToPanningScalingStateAndClear() {
+ protected void transitToPanningScalingStateAndClear() {
transitionTo(mPanningScalingState);
clear();
}
- private void transitToSinglePanningStateAndClear() {
+ protected void transitToSinglePanningStateAndClear() {
transitionTo(mSinglePanningState);
clear();
}
@@ -1016,7 +1265,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return mLastDown != null;
}
- private long timeBetween(@Nullable MotionEvent a, @Nullable MotionEvent b) {
+ protected long timeBetween(@Nullable MotionEvent a, @Nullable MotionEvent b) {
if (a == null && b == null) return 0;
return abs(timeOf(a) - timeOf(b));
}
@@ -1061,13 +1310,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
- private void removePendingDelayedMessages() {
+ protected void removePendingDelayedMessages() {
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
}
- private void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
+ protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
if (event.getActionMasked() == ACTION_DOWN) {
mPreLastDown = mLastDown;
@@ -1090,7 +1339,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
- private void sendDelayedMotionEvents() {
+ protected void sendDelayedMotionEvents() {
if (mDelayedEventQueue == null) {
return;
}
@@ -1112,7 +1361,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} while (mDelayedEventQueue != null);
}
- private void clearDelayedMotionEvents() {
+ protected void clearDelayedMotionEvents() {
while (mDelayedEventQueue != null) {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
@@ -1136,7 +1385,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
* 1. direct three tap gesture
* 2. one tap while shortcut triggered (it counts as two taps).
*/
- private void onTripleTap(MotionEvent up) {
+ protected void onTripleTap(MotionEvent up) {
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTap(); delayed: "
+ MotionEventInfo.toString(mDelayedEventQueue));
@@ -1156,7 +1405,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
clear();
}
- private boolean isActivated() {
+ protected boolean isActivated() {
return mFullScreenMagnificationController.isActivated(mDisplayId);
}
@@ -1167,6 +1416,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// Only log the 3tap and hold event
if (!shortcutTriggered) {
+ // TODO:(b/309534286): Add metrics for two-finger triple-tap and fix
+ // the log two-finger bug before enabling the flag
// Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index 6c6394faa09c..f0c44d64f5ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -35,15 +35,15 @@ import com.android.server.accessibility.AccessibilityTraceManager;
/**
* A wrapper of {@link IWindowMagnificationConnection}.
*/
-class WindowMagnificationConnectionWrapper {
+class MagnificationConnectionWrapper {
private static final boolean DBG = false;
- private static final String TAG = "WindowMagnificationConnectionWrapper";
+ private static final String TAG = "MagnificationConnectionWrapper";
private final @NonNull IWindowMagnificationConnection mConnection;
private final @NonNull AccessibilityTraceManager mTrace;
- WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+ MagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
@NonNull AccessibilityTraceManager trace) {
mConnection = connection;
mTrace = trace;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 816f22f8c7b0..3ea805bbb4a6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -60,7 +60,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
- * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}
+ * A class to manipulate window magnification through {@link MagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
* SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
* The applied magnification scale is constrained by
@@ -133,7 +133,7 @@ public class WindowMagnificationManager implements
@VisibleForTesting
@GuardedBy("mLock")
@Nullable
- WindowMagnificationConnectionWrapper mConnectionWrapper;
+ MagnificationConnectionWrapper mConnectionWrapper;
@GuardedBy("mLock")
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
@@ -245,7 +245,7 @@ public class WindowMagnificationManager implements
}
}
if (connection != null) {
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
+ mConnectionWrapper = new MagnificationConnectionWrapper(connection, mTrace);
}
if (mConnectionWrapper != null) {
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index b086406a2ad5..acb5911c8868 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,5 +19,16 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [":services.backup-sources"],
libs: ["services.core"],
- static_libs: ["app-compat-annotations"],
+ static_libs: ["app-compat-annotations", "backup_flags_lib"],
+}
+
+aconfig_declarations {
+ name: "backup_flags",
+ package: "com.android.server.backup",
+ srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "backup_flags_lib",
+ aconfig_declarations: "backup_flags",
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
new file mode 100644
index 000000000000..d695d36db0ea
--- /dev/null
+++ b/services/backup/flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.backup"
+
+flag {
+ name: "enable_skipping_restore_launched_apps"
+ namespace: "onboarding"
+ description: "Enforce behavior determined by BackupTransport implementation on whether to skip "
+ "restore for apps that have been launched."
+ bug: "308401499"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 70d7fac09a4f..9f7b62763ed3 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -24,6 +24,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
@@ -32,11 +33,15 @@ import android.app.backup.RestoreSet;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
@@ -296,12 +301,26 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
return -1;
}
- private BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
+ @VisibleForTesting
+ BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
// TODO(b/182986784): Remove device name comparison once a designated field for operation
// type is added to RestoreSet object.
int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD;
- return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+
+ if (!Flags.enableSkippingRestoreLaunchedApps()) {
+ return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+ }
+
+ boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags
+ & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0;
+
+ return new BackupEligibilityRules(mBackupManagerService.getPackageManager(),
+ LocalServices.getService(PackageManagerInternal.class),
+ mUserId,
+ mBackupManagerService.getContext(),
+ backupDestination,
+ skipRestoreForLaunchedApps);
}
public synchronized int restorePackage(String packageName, IRestoreObserver observer,
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 bbec79d6cd47..96a873ecc217 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -63,6 +63,7 @@ import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
+import com.android.server.backup.Flags;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -263,7 +264,14 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
continue;
}
- if (backupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) {
+
+ ApplicationInfo applicationInfo = info.applicationInfo;
+ if (backupEligibilityRules.appIsEligibleForBackup(applicationInfo)) {
+ if (Flags.enableSkippingRestoreLaunchedApps()
+ && !backupEligibilityRules.isAppEligibleForRestore(applicationInfo)) {
+ continue;
+ }
+
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 7c47f1e477b6..f24a3c1afc86 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -80,6 +80,7 @@ public class BackupEligibilityRules {
private final int mUserId;
private boolean mIsProfileUser = false;
@BackupDestination private final int mBackupDestination;
+ private final boolean mSkipRestoreForLaunchedApps;
/**
* When this change is enabled, {@code adb backup} is automatically turned on for apps
@@ -112,12 +113,23 @@ public class BackupEligibilityRules {
int userId,
Context context,
@BackupDestination int backupDestination) {
+ this(packageManager, packageManagerInternal, userId, context, backupDestination,
+ /* skipRestoreForLaunchedApps */ false);
+ }
+
+ public BackupEligibilityRules(PackageManager packageManager,
+ PackageManagerInternal packageManagerInternal,
+ int userId,
+ Context context,
+ @BackupDestination int backupDestination,
+ boolean skipRestoreForLaunchedApps) {
mPackageManager = packageManager;
mPackageManagerInternal = packageManagerInternal;
mUserId = userId;
mBackupDestination = backupDestination;
UserManager userManager = context.getSystemService(UserManager.class);
mIsProfileUser = userManager.isProfile();
+ mSkipRestoreForLaunchedApps = skipRestoreForLaunchedApps;
}
/**
@@ -132,6 +144,9 @@ public class BackupEligibilityRules {
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*
+ * These eligibility conditions are also checked before restore, in case the backup happened on
+ * a device / from the version of the app where these rules were not enforced.
+ *
* However, the above eligibility rules are ignored for non-system apps in in case of
* device-to-device migration, see {@link BackupDestination}.
*/
@@ -283,6 +298,27 @@ public class BackupEligibilityRules {
}
}
+ /**
+ * Determine if data restore should be run for the given package.
+ *
+ * <p>This is used in combination with {@link #appIsEligibleForBackup(ApplicationInfo)} that
+ * checks whether the backup being restored should have happened in the first place.</p>
+ */
+ public boolean isAppEligibleForRestore(ApplicationInfo app) {
+ if (!mSkipRestoreForLaunchedApps) {
+ return true;
+ }
+
+ // If an app implemented a BackupAgent, they are expected to handle being restored even
+ // after first launch and avoid conflicts between existing app data and restored data.
+ if (app.backupAgentName != null) {
+ return true;
+ }
+
+ // Otherwise only restore an app if it hasn't been launched before.
+ return !mPackageManagerInternal.wasPackageEverLaunched(app.packageName, mUserId);
+ }
+
/** Avoid backups of 'disabled' apps. */
@VisibleForTesting
boolean appIsDisabled(
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index fb8db21c3090..550e17be276d 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -21,7 +21,6 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [
":services.companion-sources",
- ":VirtualCamera-aidl-sources",
],
libs: [
"app-compat-annotations",
@@ -30,13 +29,6 @@ java_library_static {
static_libs: [
"ukey2_jni",
"virtualdevice_flags_lib",
+ "virtual_camera_service_aidl-java",
],
}
-
-filegroup {
- name: "VirtualCamera-aidl-sources",
- srcs: [
- "java/com/android/server/companion/virtual/camera/*.aidl",
- ],
- path: "java",
-}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 2c608930b391..b9c269c91651 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -522,7 +522,8 @@ public class CompanionDeviceManagerService extends SystemService {
private void notifyListeners(
@UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
mListeners.broadcast((listener, callbackUserId) -> {
- if ((int) callbackUserId == userId) {
+ int listenerUserId = (int) callbackUserId;
+ if (listenerUserId == userId || listenerUserId == UserHandle.USER_ALL) {
try {
listener.onAssociationsChanged(associations);
} catch (RemoteException ignored) {
@@ -660,6 +661,9 @@ public class CompanionDeviceManagerService extends SystemService {
enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId);
+ if (userId == UserHandle.USER_ALL) {
+ return List.copyOf(mAssociationStore.getAssociations());
+ }
return mAssociationStore.getAssociationsForUser(userId);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 1f6261383961..23e7ce68c1d0 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -20,6 +20,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
+import android.companion.Flags;
import android.companion.Telecom;
import android.companion.datatransfer.PermissionSyncRequest;
import android.net.MacAddress;
@@ -65,7 +66,14 @@ class CompanionDeviceShellCommand extends ShellCommand {
public int onCommand(String cmd) {
final PrintWriter out = getOutPrintWriter();
final int associationId;
+
try {
+ if ("simulate-device-event".equals(cmd) && Flags.devicePresence()) {
+ associationId = getNextIntArgRequired();
+ int event = getNextIntArgRequired();
+ mDevicePresenceMonitor.simulateDeviceEvent(associationId, event);
+ return 0;
+ }
switch (cmd) {
case "list": {
final int userId = getNextIntArgRequired();
@@ -107,10 +115,15 @@ class CompanionDeviceShellCommand extends ShellCommand {
mService.loadAssociationsFromDisk();
break;
- case "simulate-device-event":
+ case "simulate-device-appeared":
+ associationId = getNextIntArgRequired();
+ mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 0);
+ break;
+
+ case "simulate-device-disappeared":
associationId = getNextIntArgRequired();
- int event = getNextIntArgRequired();
- mDevicePresenceMonitor.simulateDeviceEvent(associationId, event);
+ mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1);
+ break;
case "remove-inactive-associations": {
// This command should trigger the same "clean-up" job as performed by the
@@ -346,9 +359,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
- pw.println(" simulate-device-event ASSOCIATION_ID EVENT");
- pw.println(" Simulate the companion device event changes:");
- pw.println(" Case(0): ");
+ pw.println(" simulate-device-appeared ASSOCIATION_ID");
pw.println(" Make CDM act as if the given companion device has appeared.");
pw.println(" I.e. bind the associated companion application's");
pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
@@ -356,18 +367,43 @@ class CompanionDeviceShellCommand extends ShellCommand {
pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'");
pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out"
+ ".");
- pw.println(" Case(1): ");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+ pw.println(" simulate-device-disappeared ASSOCIATION_ID");
pw.println(" Make CDM act as if the given companion device has disappeared.");
pw.println(" I.e. unbind the associated companion application's");
pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback.");
pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was");
pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than");
pw.println(" 60 seconds ago.");
- pw.println(" Case(2): ");
- pw.println(" Make CDM act as if the given companion device is BT connected ");
- pw.println(" Case(3): ");
- pw.println(" Make CDM act as if the given companion device is BT disconnected ");
- pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+ if (Flags.devicePresence()) {
+ pw.println(" simulate-device-event ASSOCIATION_ID EVENT");
+ pw.println(" Simulate the companion device event changes:");
+ pw.println(" Case(0): ");
+ pw.println(" Make CDM act as if the given companion device has appeared.");
+ pw.println(" I.e. bind the associated companion application's");
+ pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
+ pw.println(" The CDM will consider the devices as present for"
+ + "60 seconds and then");
+ pw.println(" will act as if device disappeared, unless"
+ + "'simulate-device-disappeared'");
+ pw.println(" or 'simulate-device-appeared' is called again before 60 seconds"
+ + "run out.");
+ pw.println(" Case(1): ");
+ pw.println(" Make CDM act as if the given companion device has disappeared.");
+ pw.println(" I.e. unbind the associated companion application's");
+ pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared()"
+ + "callback.");
+ pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was");
+ pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than");
+ pw.println(" 60 seconds ago.");
+ pw.println(" Case(2): ");
+ pw.println(" Make CDM act as if the given companion device is BT connected ");
+ pw.println(" Case(3): ");
+ pw.println(" Make CDM act as if the given companion device is BT disconnected ");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+ }
pw.println(" remove-inactive-associations");
pw.println(" Remove self-managed associations that have not been active ");
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 8fea078c3183..e42b9356cca3 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -79,7 +79,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
void onDeviceDisappeared(int associationId);
/**Invoked when device has corresponding event changes. */
- void onDeviceEvent(int associationId, int state);
+ void onDeviceEvent(int associationId, int event);
}
private final @NonNull AssociationStore mAssociationStore;
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 eeaa423b1aef..c111ec3213d9 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -49,7 +49,6 @@ import com.android.server.input.InputManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
@@ -83,14 +82,6 @@ class InputController {
@interface PhysType {
}
- /**
- * The maximum length of a device name (in bytes in UTF-8 encoding).
- *
- * This limitation comes directly from uinput.
- * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h
- */
- private static final int DEVICE_NAME_MAX_LENGTH = 80;
-
final Object mLock = new Object();
/* Token -> file descriptor associations. */
@@ -138,25 +129,17 @@ class InputController {
}
}
- void createDpad(@NonNull String deviceName,
- int vendorId,
- int productId,
- @NonNull IBinder deviceToken,
- int displayId) {
+ void createDpad(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
final String phys = createPhys(PHYS_TYPE_DPAD);
- try {
- createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId,
+ createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId,
productId, deviceToken, displayId, phys,
() -> mNativeWrapper.openUinputDpad(deviceName, vendorId, productId, phys));
- } catch (DeviceCreationException e) {
- throw new RuntimeException(
- "Failed to create virtual dpad device '" + deviceName + "'.", e);
- }
}
void createKeyboard(@NonNull String deviceName, int vendorId, int productId,
@NonNull IBinder deviceToken, int displayId, @NonNull String languageTag,
- @NonNull String layoutType) {
+ @NonNull String layoutType) throws DeviceCreationException {
final String phys = createPhys(PHYS_TYPE_KEYBOARD);
mInputManagerInternal.addKeyboardLayoutAssociation(phys, languageTag,
layoutType);
@@ -166,66 +149,42 @@ class InputController {
() -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys));
} catch (DeviceCreationException e) {
mInputManagerInternal.removeKeyboardLayoutAssociation(phys);
- throw new RuntimeException(
- "Failed to create virtual keyboard device '" + deviceName + "'.", e);
+ throw e;
}
}
- void createMouse(@NonNull String deviceName,
- int vendorId,
- int productId,
- @NonNull IBinder deviceToken,
- int displayId) {
+ void createMouse(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
final String phys = createPhys(PHYS_TYPE_MOUSE);
- try {
- createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId,
- deviceToken, displayId, phys,
- () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys));
- } catch (DeviceCreationException e) {
- throw new RuntimeException(
- "Failed to create virtual mouse device: '" + deviceName + "'.", e);
- }
+ createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId,
+ deviceToken, displayId, phys,
+ () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys));
mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
}
- void createTouchscreen(@NonNull String deviceName,
- int vendorId,
- int productId,
- @NonNull IBinder deviceToken,
- int displayId,
- int height,
- int width) {
+ void createTouchscreen(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId, int height, int width)
+ throws DeviceCreationException {
final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
- try {
- createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId,
- productId, deviceToken, displayId, phys,
- () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
- phys, height, width));
- } catch (DeviceCreationException e) {
- throw new RuntimeException(
- "Failed to create virtual touchscreen device '" + deviceName + "'.", e);
- }
+ createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId,
+ productId, deviceToken, displayId, phys,
+ () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
+ height, width));
}
- void createNavigationTouchpad(
- @NonNull String deviceName,
- int vendorId,
- int productId,
- @NonNull IBinder deviceToken,
- int displayId,
- int touchpadHeight,
- int touchpadWidth) {
+ void createNavigationTouchpad(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId, int height, int width)
+ throws DeviceCreationException {
final String phys = createPhys(PHYS_TYPE_NAVIGATION_TOUCHPAD);
mInputManagerInternal.setTypeAssociation(phys, NAVIGATION_TOUCHPAD_DEVICE_TYPE);
try {
createDeviceInternal(InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD, deviceName,
vendorId, productId, deviceToken, displayId, phys,
() -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
- phys, touchpadHeight, touchpadWidth));
+ phys, height, width));
} catch (DeviceCreationException e) {
mInputManagerInternal.unsetTypeAssociation(phys);
- throw new RuntimeException(
- "Failed to create virtual navigation touchpad device '" + deviceName + "'.", e);
+ throw e;
}
}
@@ -234,10 +193,10 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not unregister input device for given token");
+ Slog.w(TAG, "Could not unregister input device for given token.");
+ } else {
+ closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
- closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
}
@@ -326,21 +285,11 @@ class InputController {
}
/**
- * Validates a device name by checking length and whether a device with the same name
- * already exists. Throws exceptions if the validation fails.
+ * Validates a device name by checking whether a device with the same name already exists.
* @param deviceName The name of the device to be validated
* @throws DeviceCreationException if {@code deviceName} is not valid.
*/
private void validateDeviceName(String deviceName) throws DeviceCreationException {
- // Comparison is greater or equal because the device name must fit into a const char*
- // including the \0-terminator. Therefore the actual number of bytes that can be used
- // for device name is DEVICE_NAME_MAX_LENGTH - 1
- if (deviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) {
- throw new DeviceCreationException(
- "Input device name exceeds maximum length of " + DEVICE_NAME_MAX_LENGTH
- + "bytes: " + deviceName);
- }
-
synchronized (mLock) {
for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) {
if (mInputDeviceDescriptors.valueAt(i).mName.equals(deviceName)) {
@@ -365,8 +314,7 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send key event to input device for given token");
+ return false;
}
return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
@@ -378,8 +326,7 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send key event to input device for given token");
+ return false;
}
return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
@@ -391,13 +338,12 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send button event to input device for given token");
+ return false;
}
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- throw new IllegalStateException(
- "Display id associated with this mouse is not currently targetable");
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
+ inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
@@ -409,8 +355,7 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send touch event to input device for given token");
+ return false;
}
return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
@@ -424,13 +369,12 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send relative event to input device for given token");
+ return false;
}
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- throw new IllegalStateException(
- "Display id associated with this mouse is not currently targetable");
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
+ inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos());
@@ -442,13 +386,12 @@ class InputController {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
token);
if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException(
- "Could not send scroll event to input device for given token");
+ return false;
}
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- throw new IllegalStateException(
- "Display id associated with this mouse is not currently targetable");
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
+ inputDeviceDescriptor.getDisplayId());
}
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos());
@@ -465,8 +408,8 @@ class InputController {
}
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
- throw new IllegalStateException(
- "Display id associated with this mouse is not currently targetable");
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
+ inputDeviceDescriptor.getDisplayId());
}
return LocalServices.getService(InputManagerInternal.class).getCursorPosition();
}
@@ -758,13 +701,19 @@ class InputController {
}
/** An internal exception that is thrown to indicate an error when opening a virtual device. */
- private static class DeviceCreationException extends Exception {
+ static class DeviceCreationException extends Exception {
+ DeviceCreationException() {
+ super();
+ }
DeviceCreationException(String message) {
super(message);
}
- DeviceCreationException(String message, Exception cause) {
+ DeviceCreationException(String message, Throwable cause) {
super(message, cause);
}
+ DeviceCreationException(Throwable cause) {
+ super(cause);
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 83143a431406..5295ec82e3c3 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1171888
+
set noparent
ogunwale@google.com
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 ae8fddfd35ef..e6bfeb79fafb 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.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.
@@ -51,7 +51,7 @@ import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
-import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
@@ -277,7 +277,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
runningAppsChangedCallback,
params,
DisplayManagerGlobal.getInstance(),
- Flags.virtualCamera() ? new VirtualCameraController(context) : null);
+ Flags.virtualCamera() ? new VirtualCameraController() : null);
}
@VisibleForTesting
@@ -304,7 +304,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
- mPersistentDeviceId = PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationInfo.getId();
+ mPersistentDeviceId = createPersistentDeviceId(associationInfo.getId());
mService = service;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -380,6 +380,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return mSensorController;
}
+ static String createPersistentDeviceId(int associationId) {
+ return PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationId;
+ }
+
/**
* Returns the flags that should be added to any virtual displays created on this virtual
* device.
@@ -688,7 +692,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId());
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -700,14 +707,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
super.createVirtualKeyboard_enforcePermission();
Objects.requireNonNull(config);
checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
- synchronized (mVirtualDeviceLock) {
- mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag());
- }
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createKeyboard(config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
config.getLanguageTag(), config.getLayoutType());
+ synchronized (mVirtualDeviceLock) {
+ mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag());
+ }
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -723,6 +733,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
try {
mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(),
config.getProductId(), deviceToken, config.getAssociatedDisplayId());
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -735,19 +747,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
super.createVirtualTouchscreen_enforcePermission();
Objects.requireNonNull(config);
checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
- int screenHeight = config.getHeight();
- int screenWidth = config.getWidth();
- if (screenHeight <= 0 || screenWidth <= 0) {
- throw new IllegalArgumentException(
- "Cannot create a virtual touchscreen, screen dimensions must be positive. Got: "
- + "(" + screenWidth + ", " + screenHeight + ")");
- }
-
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createTouchscreen(config.getInputDeviceName(), config.getVendorId(),
config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
- screenHeight, screenWidth);
+ config.getHeight(), config.getWidth());
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -760,20 +766,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
super.createVirtualNavigationTouchpad_enforcePermission();
Objects.requireNonNull(config);
checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
- int touchpadHeight = config.getHeight();
- int touchpadWidth = config.getWidth();
- if (touchpadHeight <= 0 || touchpadWidth <= 0) {
- throw new IllegalArgumentException(
- "Cannot create a virtual navigation touchpad, touchpad dimensions must be positive."
- + " Got: (" + touchpadHeight + ", " + touchpadWidth + ")");
- }
-
final long ident = Binder.clearCallingIdentity();
try {
mInputController.createNavigationTouchpad(
config.getInputDeviceName(), config.getVendorId(),
- config.getProductId(), deviceToken, config.getAssociatedDisplayId(),
- touchpadHeight, touchpadWidth);
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()),
+ config.getHeight(), config.getWidth());
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -950,13 +951,28 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ @Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
- public void registerVirtualCamera(@NonNull IVirtualCamera camera) {
+ public void registerVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
super.registerVirtualCamera_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
if (mVirtualCameraController == null) {
- return;
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
+ }
+ mVirtualCameraController.registerCamera(Objects.requireNonNull(cameraConfig));
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void unregisterVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ super.unregisterVirtualCamera_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
+ if (mVirtualCameraController == null) {
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(Objects.requireNonNull(camera));
+ mVirtualCameraController.unregisterCamera(Objects.requireNonNull(cameraConfig));
}
@Override
@@ -983,6 +999,20 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ // For display mirroring, we want to dispatch all key events to the source (default) display,
+ // as the virtual display doesn't have any focused windows. Hence, call this for
+ // associating any input device to the source display if the input device emits any key events.
+ private int getTargetDisplayIdForInput(int displayId) {
+ if (!Flags.interactiveScreenMirror()) {
+ return displayId;
+ }
+
+ DisplayManagerInternal displayManager = LocalServices.getService(
+ DisplayManagerInternal.class);
+ int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId);
+ return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId;
+ }
+
@GuardedBy("mVirtualDeviceLock")
private GenericWindowPolicyController createWindowPolicyControllerLocked(
@NonNull Set<String> displayCategories) {
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 92af68bc40a3..215970eedff1 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -27,6 +27,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -94,6 +95,11 @@ public class VirtualDeviceManagerService extends SystemService {
private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native";
+ private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerNativeImpl mNativeImpl;
@@ -105,6 +111,9 @@ public class VirtualDeviceManagerService extends SystemService {
private static AtomicInteger sNextUniqueIndex = new AtomicInteger(
Context.DEVICE_ID_DEFAULT + 1);
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private List<AssociationInfo> mActiveAssociations = new ArrayList<>();
+
private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
new CompanionDeviceManager.OnAssociationsChangedListener() {
@Override
@@ -161,6 +170,7 @@ public class VirtualDeviceManagerService extends SystemService {
};
@Override
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
if (Flags.enableNativeVdm()) {
@@ -172,6 +182,21 @@ public class VirtualDeviceManagerService extends SystemService {
activityTaskManagerInternal.registerActivityStartInterceptor(
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
mActivityInterceptorCallback);
+
+ if (Flags.persistentDeviceIdApi()) {
+ CompanionDeviceManager cdm =
+ getContext().getSystemService(CompanionDeviceManager.class);
+ if (cdm != null) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mActiveAssociations = cdm.getAllAssociations(UserHandle.USER_ALL);
+ }
+ cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+ this::onCdmAssociationsChanged, UserHandle.USER_ALL);
+ } else {
+ Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
+ + " will be available.");
+ }
+ }
}
void onCameraAccessBlocked(int appUid) {
@@ -264,9 +289,11 @@ public class VirtualDeviceManagerService extends SystemService {
try {
getContext().sendBroadcastAsUser(i, UserHandle.ALL);
- synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
- unregisterCdmAssociationListener();
+ if (!Flags.persistentDeviceIdApi()) {
+ synchronized (mVirtualDeviceManagerLock) {
+ if (mVirtualDevices.size() == 0) {
+ unregisterCdmAssociationListener();
+ }
}
}
} finally {
@@ -316,6 +343,45 @@ public class VirtualDeviceManagerService extends SystemService {
cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ void onCdmAssociationsChanged(List<AssociationInfo> associations) {
+ Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
+ Set<String> removedPersistentDeviceIds = new HashSet<>();
+ synchronized (mVirtualDeviceManagerLock) {
+ Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
+ for (int i = 0; i < associations.size(); ++i) {
+ activeAssociationIds.add(associations.get(i).getId());
+ }
+
+ for (int i = 0; i < mActiveAssociations.size(); ++i) {
+ AssociationInfo associationInfo = mActiveAssociations.get(i);
+ if (!activeAssociationIds.contains(associationInfo.getId())
+ && VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+ associationInfo.getDeviceProfile())) {
+ removedPersistentDeviceIds.add(
+ VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
+ }
+ }
+
+ for (int i = 0; i < mVirtualDevices.size(); i++) {
+ VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
+ if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) {
+ virtualDevicesToRemove.add(virtualDevice);
+ }
+ }
+
+ mActiveAssociations = associations;
+ }
+
+ for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
+ virtualDevice.close();
+ }
+
+ if (!removedPersistentDeviceIds.isEmpty()) {
+ mLocalService.onPersistentDeviceIdsRemoved(removedPersistentDeviceIds);
+ }
+ }
+
private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() {
synchronized (mVirtualDeviceManagerLock) {
ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size());
@@ -393,7 +459,7 @@ public class VirtualDeviceManagerService extends SystemService {
}
synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
+ if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
final long callindId = Binder.clearCallingIdentity();
try {
registerCdmAssociationListener();
@@ -441,10 +507,8 @@ public class VirtualDeviceManagerService extends SystemService {
+ " is not the owner of the supplied VirtualDevice");
}
- int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback,
- packageName);
- mLocalService.onVirtualDisplayCreated(displayId);
- return displayId;
+ return virtualDeviceImpl.createVirtualDisplay(
+ virtualDisplayConfig, callback, packageName);
}
@Override // Binder call
@@ -625,11 +689,12 @@ public class VirtualDeviceManagerService extends SystemService {
private final class LocalService extends VirtualDeviceManagerInternal {
@GuardedBy("mVirtualDeviceManagerLock")
- private final ArrayList<VirtualDisplayListener>
- mVirtualDisplayListeners = new ArrayList<>();
+ private final ArrayList<AppsOnVirtualDeviceListener> mAppsOnVirtualDeviceListeners =
+ new ArrayList<>();
@GuardedBy("mVirtualDeviceManagerLock")
- private final ArrayList<AppsOnVirtualDeviceListener>
- mAppsOnVirtualDeviceListeners = new ArrayList<>();
+ private final ArrayList<Consumer<String>> mPersistentDeviceIdRemovedListeners =
+ new ArrayList<>();
+
@GuardedBy("mVirtualDeviceManagerLock")
private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
@@ -665,35 +730,15 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
- public void onVirtualDisplayCreated(int displayId) {
- final VirtualDisplayListener[] listeners;
- synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
- }
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayCreated(displayId);
- }
- });
- }
-
- @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
- final VirtualDisplayListener[] listeners;
VirtualDeviceImpl virtualDeviceImpl;
synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
virtualDeviceImpl = mVirtualDevices.get(
((VirtualDeviceImpl) virtualDevice).getDeviceId());
}
if (virtualDeviceImpl != null) {
virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
}
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayRemoved(displayId);
- }
- });
}
@Override
@@ -725,6 +770,22 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds) {
+ final List<Consumer<String>> persistentDeviceIdRemovedListeners;
+ synchronized (mVirtualDeviceManagerLock) {
+ persistentDeviceIdRemovedListeners = List.copyOf(
+ mPersistentDeviceIdRemovedListeners);
+ }
+ mHandler.post(() -> {
+ for (String persistentDeviceId : removedPersistentDeviceIds) {
+ for (Consumer<String> listener : persistentDeviceIdRemovedListeners) {
+ listener.accept(persistentDeviceId);
+ }
+ }
+ });
+ }
+
+ @Override
public void onAuthenticationPrompt(int uid) {
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mVirtualDevices.size(); i++) {
@@ -791,6 +852,10 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
+ if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ }
+
VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
virtualDevice = mVirtualDevices.get(deviceId);
@@ -799,34 +864,34 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
- public void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
+ public void registerAppsOnVirtualDeviceListener(
+ @NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.add(listener);
+ mAppsOnVirtualDeviceListeners.add(listener);
}
}
@Override
- public void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
+ public void unregisterAppsOnVirtualDeviceListener(
+ @NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.remove(listener);
+ mAppsOnVirtualDeviceListeners.remove(listener);
}
}
@Override
- public void registerAppsOnVirtualDeviceListener(
- @NonNull AppsOnVirtualDeviceListener listener) {
+ public void registerPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
synchronized (mVirtualDeviceManagerLock) {
- mAppsOnVirtualDeviceListeners.add(listener);
+ mPersistentDeviceIdRemovedListeners.add(persistentDeviceIdRemovedListener);
}
}
@Override
- public void unregisterAppsOnVirtualDeviceListener(
- @NonNull AppsOnVirtualDeviceListener listener) {
+ public void unregisterPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
synchronized (mVirtualDeviceManagerLock) {
- mAppsOnVirtualDeviceListeners.remove(listener);
+ mPersistentDeviceIdRemovedListeners.remove(persistentDeviceIdRemovedListener);
}
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl b/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
deleted file mode 100644
index a4c1c4249697..000000000000
--- a/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
+++ /dev/null
@@ -1,39 +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.server.companion.virtual.camera;
-
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-
-/**
- * AIDL Interface to communicate with the VirtualCamera HAL
- * @hide
- */
-interface IVirtualCameraService {
-
- /**
- * Registers a new camera with the virtual camera hal.
- * @return true if the camera was successfully registered
- */
- boolean registerCamera(in IVirtualCamera camera);
-
- /**
- * Unregisters the camera from the virtual camera hal. After this call the virtual camera won't
- * be visible to the camera framework anymore.
- */
- void unregisterCamera(in IVirtualCamera camera);
-}
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 031d94962844..06be3f39dcd1 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
@@ -16,207 +16,127 @@
package com.android.server.companion.virtual.camera;
+import static com.android.server.companion.virtual.camera.VirtualCameraConversionUtil.getServiceCameraConfiguration;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Set;
/**
* Manages the registration and removal of virtual camera from the server side.
*
* <p>This classes delegate calls to the virtual camera service, so it is dependent on the service
- * to be up and running
+ * to be up and running.
*/
-public class VirtualCameraController implements IBinder.DeathRecipient, ServiceConnection {
-
- private static class VirtualCameraInfo {
-
- private final IVirtualCamera mVirtualCamera;
- private boolean mIsRegistered;
-
- VirtualCameraInfo(IVirtualCamera virtualCamera) {
- mVirtualCamera = virtualCamera;
- }
- }
+public final class VirtualCameraController implements IBinder.DeathRecipient {
+ private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
private static final String TAG = "VirtualCameraController";
- private static final String VIRTUAL_CAMERA_SERVICE_PACKAGE = "com.android.virtualcamera";
- private static final String VIRTUAL_CAMERA_SERVICE_CLASS = ".VirtualCameraService";
- private final Context mContext;
-
- @Nullable private IVirtualCameraService mVirtualCameraService = null;
+ @Nullable private IVirtualCameraService mVirtualCameraService;
@GuardedBy("mCameras")
- private final Map<IVirtualCamera, VirtualCameraInfo> mCameras = new HashMap<>(1);
+ private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
- public VirtualCameraController(Context context) {
- mContext = context;
+ public VirtualCameraController() {
connectVirtualCameraService();
}
- private void connectVirtualCameraService() {
- final long callingId = Binder.clearCallingIdentity();
- try {
- Intent intent = new Intent();
- intent.setPackage(VIRTUAL_CAMERA_SERVICE_PACKAGE);
- intent.setComponent(
- ComponentName.createRelative(
- VIRTUAL_CAMERA_SERVICE_PACKAGE, VIRTUAL_CAMERA_SERVICE_CLASS));
- mContext.startServiceAsUser(intent, UserHandle.SYSTEM);
- if (!mContext.bindServiceAsUser(
- intent,
- this,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.SYSTEM)) {
- mContext.unbindService(this);
- Log.w(
- TAG,
- "connectVirtualCameraService: Failed to connect to the virtual camera "
- + "service");
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- private void forwardPendingRegistrations() {
- IVirtualCameraService cameraService = mVirtualCameraService;
- if (cameraService == null) {
- return;
- }
- synchronized (mCameras) {
- for (VirtualCameraInfo cameraInfo : mCameras.values()) {
- if (cameraInfo.mIsRegistered) {
- continue;
- }
- try {
- cameraService.registerCamera(cameraInfo.mVirtualCamera);
- cameraInfo.mIsRegistered = true;
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- }
+ @VisibleForTesting
+ VirtualCameraController(IVirtualCameraService virtualCameraService) {
+ mVirtualCameraService = virtualCameraService;
}
/**
- * Remove the virtual camera with the provided name
+ * Register a new virtual camera with the given config.
*
- * @param camera The name of the camera to remove
+ * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
- public void unregisterCamera(@NonNull IVirtualCamera camera) {
- IVirtualCameraService virtualCameraService = mVirtualCameraService;
- if (virtualCameraService != null) {
- try {
- virtualCameraService.unregisterCamera(camera);
+ public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ // Try to connect to service if not connected already.
+ if (mVirtualCameraService == null) {
+ connectVirtualCameraService();
+ }
+ // Throw exception if we are unable to connect to service.
+ if (mVirtualCameraService == null) {
+ throw new IllegalStateException("Virtual camera service is not connected.");
+ }
+
+ try {
+ if (registerCameraWithService(cameraConfig)) {
synchronized (mCameras) {
- VirtualCameraInfo cameraInfo = mCameras.remove(camera);
- cameraInfo.mIsRegistered = false;
+ mCameras.add(cameraConfig);
}
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ } else {
+ // TODO(b/310857519): Revisit this to find a better way of indicating failure.
+ throw new RuntimeException("Failed to register virtual camera.");
}
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
/**
- * Register a new virtual camera with the provided characteristics.
+ * Unregister the virtual camera with the given config.
*
- * @param camera The {@link IVirtualCamera} producing the image to communicate with the client.
- * @throws IllegalArgumentException if the characteristics could not be parsed.
+ * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
- public void registerCamera(@NonNull IVirtualCamera camera) {
- IVirtualCameraService service = mVirtualCameraService;
- VirtualCameraInfo virtualCameraInfo = new VirtualCameraInfo(camera);
- synchronized (mCameras) {
- mCameras.put(camera, virtualCameraInfo);
- }
- if (service != null) {
- try {
- if (service.registerCamera(camera)) {
- virtualCameraInfo.mIsRegistered = true;
- return;
- }
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ try {
+ if (mVirtualCameraService == null) {
+ Slog.w(TAG, "Virtual camera service is not connected.");
+ } else {
+ mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
}
+ synchronized (mCameras) {
+ mCameras.remove(cameraConfig);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
-
- // Service was not available or registration failed, save the registration for later
- connectVirtualCameraService();
}
@Override
public void binderDied() {
- Log.d(TAG, "binderDied");
+ Slog.d(TAG, "Virtual camera service died.");
mVirtualCameraService = null;
- }
-
- @Override
- public void onBindingDied(ComponentName name) {
- mVirtualCameraService = null;
- Log.d(TAG, "onBindingDied() called with: name = [" + name + "]");
- }
-
- @Override
- public void onNullBinding(ComponentName name) {
- mVirtualCameraService = null;
- Log.d(TAG, "onNullBinding() called with: name = [" + name + "]");
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "onServiceConnected: " + name.toString());
- mVirtualCameraService = IVirtualCameraService.Stub.asInterface(service);
- try {
- service.linkToDeath(this, 0);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ synchronized (mCameras) {
+ mCameras.clear();
}
- forwardPendingRegistrations();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "onServiceDisconnected() called with: name = [" + name + "]");
- mVirtualCameraService = null;
}
/** Release resources associated with this controller. */
public void close() {
- if (mVirtualCameraService == null) {
- return;
- }
synchronized (mCameras) {
- mCameras.forEach(
- (name, cameraInfo) -> {
- try {
- mVirtualCameraService.unregisterCamera(name);
- } catch (RemoteException e) {
- Log.w(
- TAG,
- "close(): Camera failed to be removed on camera service.",
- e);
- }
- });
+ if (mVirtualCameraService == null) {
+ Slog.w(TAG, "Virtual camera service is not connected.");
+ } else {
+ for (VirtualCameraConfig config : mCameras) {
+ try {
+ mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "close(): Camera failed to be removed on camera "
+ + "service.", e);
+ }
+ }
+ }
+ mCameras.clear();
}
- mContext.unbindService(this);
+ mVirtualCameraService = null;
}
/** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
@@ -226,20 +146,34 @@ public class VirtualCameraController implements IBinder.DeathRecipient, ServiceC
fout.printf("%sService:%s\n", indent, mVirtualCameraService);
synchronized (mCameras) {
fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
- for (VirtualCameraInfo info : mCameras.values()) {
- VirtualCameraHalConfig config = null;
- try {
- config = info.mVirtualCamera.getHalConfig();
- } catch (RemoteException ex) {
- Log.w(TAG, ex);
- }
- fout.printf(
- "%s- %s isRegistered: %s, token: %s\n",
- indent,
- config == null ? "" : config.displayName,
- info.mIsRegistered,
- info.mVirtualCamera);
+ for (VirtualCameraConfig config : mCameras) {
+ fout.printf("%s token: %s\n", indent, config);
+ }
+ }
+ }
+
+ private void connectVirtualCameraService() {
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ IBinder virtualCameraBinder =
+ ServiceManager.waitForService(VIRTUAL_CAMERA_SERVICE_NAME);
+ if (virtualCameraBinder == null) {
+ Slog.e(TAG, "connectVirtualCameraService: Failed to connect to the virtual "
+ + "camera service");
+ return;
}
+ virtualCameraBinder.linkToDeath(this, 0);
+ mVirtualCameraService = IVirtualCameraService.Stub.asInterface(virtualCameraBinder);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
+
+ private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
+ VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
+ return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+ serviceConfiguration);
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
new file mode 100644
index 000000000000..a570d0989134
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual.camera;
+
+import android.annotation.NonNull;
+import android.companion.virtual.camera.IVirtualCameraCallback;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtualcamera.Format;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.SupportedStreamConfiguration;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.graphics.ImageFormat;
+import android.os.RemoteException;
+import android.view.Surface;
+
+/** Utilities to convert the client side classes to the virtual camera service ones. */
+public final class VirtualCameraConversionUtil {
+
+ /**
+ * Fetches the configuration of the provided virtual cameraConfig that was provided by its owner
+ * and convert it into the {@link IVirtualCameraService} types: {@link
+ * VirtualCameraConfiguration}.
+ *
+ * @param cameraConfig The cameraConfig sent by the client.
+ * @return The converted configuration to be sent to the {@link IVirtualCameraService}.
+ * @throws RemoteException If there was an issue fetching the configuration from the client.
+ */
+ @NonNull
+ public static android.companion.virtualcamera.VirtualCameraConfiguration
+ getServiceCameraConfiguration(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ VirtualCameraConfiguration serviceConfiguration = new VirtualCameraConfiguration();
+
+ serviceConfiguration.supportedStreamConfigs =
+ cameraConfig.getStreamConfigs().stream()
+ .map(VirtualCameraConversionUtil::convertSupportedStreamConfiguration)
+ .toArray(SupportedStreamConfiguration[]::new);
+
+ serviceConfiguration.virtualCameraCallback = convertCallback(cameraConfig.getCallback());
+ return serviceConfiguration;
+ }
+
+ @NonNull
+ private static android.companion.virtualcamera.IVirtualCameraCallback convertCallback(
+ @NonNull IVirtualCameraCallback camera) {
+ return new android.companion.virtualcamera.IVirtualCameraCallback.Stub() {
+ @Override
+ public void onStreamConfigured(
+ int streamId, Surface surface, int width, int height, int pixelFormat)
+ throws RemoteException {
+ VirtualCameraStreamConfig streamConfig =
+ createStreamConfig(width, height, pixelFormat);
+ camera.onStreamConfigured(streamId, surface, streamConfig);
+ }
+
+ @Override
+ public void onStreamClosed(int streamId) throws RemoteException {
+ camera.onStreamClosed(streamId);
+ }
+ };
+ }
+
+ @NonNull
+ private static VirtualCameraStreamConfig createStreamConfig(
+ int width, int height, int pixelFormat) {
+ return new VirtualCameraStreamConfig(width, height, pixelFormat);
+ }
+
+ @NonNull
+ private static SupportedStreamConfiguration convertSupportedStreamConfiguration(
+ VirtualCameraStreamConfig stream) {
+ SupportedStreamConfiguration supportedConfig = new SupportedStreamConfiguration();
+ supportedConfig.height = stream.getHeight();
+ supportedConfig.width = stream.getWidth();
+ supportedConfig.pixelFormat = convertFormat(stream.getFormat());
+ return supportedConfig;
+ }
+
+ private static int convertFormat(int format) {
+ return format == ImageFormat.YUV_420_888 ? Format.YUV_420_888 : Format.UNKNOWN;
+ }
+
+ private VirtualCameraConversionUtil() {
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4e49c6e4e7de..49457fbb94f5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -103,7 +103,7 @@ java_library_static {
"android.hardware.power-java_static",
],
srcs: [
- ":android.hardware.biometrics.face-V3-java-source",
+ ":android.hardware.biometrics.face-V4-java-source",
":android.hardware.tv.hdmi.connection-V1-java-source",
":android.hardware.tv.hdmi.earc-V1-java-source",
":statslog-art-java-gen",
@@ -199,7 +199,7 @@ java_library_static {
"biometrics_flags_lib",
"am_flags_lib",
"com_android_wm_shell_flags_lib",
- "android.app.flags-aconfig-java"
+ "service-jobscheduler-deviceidle.flags-aconfig-java",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 065a447a7cd1..7e4cf4f35132 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -43,6 +43,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -54,7 +55,6 @@ import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.snapshot.PackageDataSnapshot;
@@ -1421,6 +1421,11 @@ public abstract class PackageManagerInternal {
@UserIdInt int userId);
/**
+ * Checks if package is stopped for a specific user.
+ */
+ public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId);
+
+ /**
* Sends the PACKAGE_RESTARTED broadcast.
*/
public abstract void sendPackageRestartedBroadcast(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 57ed5a298d9f..ce1a8756a849 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -51,6 +51,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.MediaStore;
import android.provider.Settings;
import android.system.ErrnoException;
@@ -61,6 +62,7 @@ import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
@@ -123,6 +125,8 @@ public final class PinnerService extends SystemService {
public @interface AppKey {}
private final Context mContext;
+ private final Injector mInjector;
+ private final DeviceConfigInterface mDeviceConfigInterface;
private final ActivityTaskManagerInternal mAtmInternal;
private final ActivityManagerInternal mAmInternal;
private final IActivityManager mAm;
@@ -154,6 +158,10 @@ public final class PinnerService extends SystemService {
@GuardedBy("this")
private ArraySet<Integer> mPinKeys;
+ // Note that we don't use the `_BOOT` namespace for anonymous pinnings, as we want
+ // them to be responsive to dynamic flag changes for experimentation.
+ private static final String DEVICE_CONFIG_NAMESPACE_ANON_SIZE =
+ DeviceConfig.NAMESPACE_RUNTIME_NATIVE;
private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size";
private static final long DEFAULT_ANON_SIZE =
SystemProperties.getLong("pinner.pin_shared_anon_size", 0);
@@ -184,21 +192,40 @@ public final class PinnerService extends SystemService {
}
};
- private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
+ private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigAnonSizeListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace())
+ if (DEVICE_CONFIG_NAMESPACE_ANON_SIZE.equals(properties.getNamespace())
&& properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) {
refreshPinAnonConfig();
}
}
};
+ /** Utility class for testing. */
+ @VisibleForTesting
+ static class Injector {
+ protected DeviceConfigInterface getDeviceConfigInterface() {
+ return DeviceConfigInterface.REAL;
+ }
+
+ protected void publishBinderService(PinnerService service, Binder binderService) {
+ service.publishBinderService("pinner", binderService);
+ }
+ }
+
public PinnerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ PinnerService(Context context, Injector injector) {
super(context);
mContext = context;
+ mInjector = injector;
+ mDeviceConfigInterface = mInjector.getDeviceConfigInterface();
mConfiguredToPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
mConfiguredToPinHome = context.getResources().getBoolean(
@@ -222,10 +249,10 @@ public final class PinnerService extends SystemService {
registerUidListener();
registerUserSetupCompleteListener();
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ mDeviceConfigInterface.addOnPropertiesChangedListener(
+ DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
new HandlerExecutor(mPinnerHandler),
- mDeviceConfigListener);
+ mDeviceConfigAnonSizeListener);
}
@Override
@@ -234,7 +261,7 @@ public final class PinnerService extends SystemService {
Slog.i(TAG, "Starting PinnerService");
}
mBinderService = new BinderService();
- publishBinderService("pinner", mBinderService);
+ mInjector.publishBinderService(this, mBinderService);
publishLocalService(PinnerService.class, this);
mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
@@ -566,7 +593,7 @@ public final class PinnerService extends SystemService {
// Pin the camera application. Default to the system property only if the experiment
// phenotype property is not set.
boolean shouldPinCamera = mConfiguredToPinCamera
- && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ && mDeviceConfigInterface.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
"pin_camera",
SystemProperties.getBoolean("pinner.pin_camera", true));
if (shouldPinCamera) {
@@ -709,8 +736,8 @@ public final class PinnerService extends SystemService {
*/
private void refreshPinAnonConfig() {
long newPinAnonSize =
- DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ mDeviceConfigInterface.getLong(
+ DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
DEVICE_CONFIG_KEY_ANON_SIZE,
DEFAULT_ANON_SIZE);
newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE));
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 15fc2dc15d02..f6835feeea16 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3651,7 +3651,26 @@ class StorageManagerService extends IStorageManager.Stub
Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
if (mMounted) {
- mVold.unmountAppFuse(uid, mountId);
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ // We need to run the unmount on a separate thread to
+ // prevent a possible deadlock, where:
+ // 1. AppFuseThread (this thread) tries to call into vold
+ // 2. the vold lock is held by another thread, which called:
+ // mVold.openAppFuseFile()
+ // as part of that call, vold calls open() on the
+ // underlying file, which is a call that needs to be
+ // handled by the AppFuseThread, which is stuck waiting
+ // for the vold lock (see 1.)
+ // It is safe to do the unmount asynchronously, because the mount
+ // path we use is never reused during the current boot cycle;
+ // see mNextAppFuseName. Also,we have anyway stopped serving
+ // requests at this point.
+ mVold.unmountAppFuse(uid, mountId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ });
mMounted = false;
}
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index cd9c53bb92b9..80c4c58cea97 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -41,6 +41,18 @@
"file_patterns": ["StorageManagerService\\.java"]
},
{
+ "name": "CtsScopedStorageBypassDatabaseOperationsTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
+ "name": "CtsScopedStorageGeneralTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
+ "name": "CtsScopedStorageRedactUriTest",
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
"name": "FrameworksMockingServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 0d423d8a0a62..2ba3a1d751d0 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -33,7 +33,6 @@ import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IVpnManager;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkStack;
import android.net.UnderlyingNetworkInfo;
@@ -437,16 +436,9 @@ public class VpnManagerService extends IVpnManager.Stub {
throw new UnsupportedOperationException("Legacy VPN is deprecated");
}
int user = UserHandle.getUserId(mDeps.getCallingUid());
- // Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
- // the code might not work well since getActiveNetwork might return null if the uid is
- // blocked by NetworkPolicyManagerService.
- final LinkProperties egress = mCm.getLinkProperties(mCm.getActiveNetwork());
- if (egress == null) {
- throw new IllegalStateException("Missing active network connection");
- }
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, null /* underlying */, egress);
+ mVpns.get(user).startLegacyVpn(profile);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9716cf69015c..3ce91c8a2b7b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1076,6 +1076,16 @@ final class ActivityManagerConstants extends ContentObserver {
public boolean APP_PROFILER_PSS_PROFILING_DISABLED;
+ /**
+ * The modifier used to adjust PSS thresholds in OomAdjuster when RSS is collected instead.
+ */
+ static final String KEY_PSS_TO_RSS_THRESHOLD_MODIFIER =
+ "pss_to_rss_threshold_modifier";
+
+ private final float mDefaultPssToRssThresholdModifier;
+
+ public float PSS_TO_RSS_THRESHOLD_MODIFIER;
+
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1254,6 +1264,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_DISABLE_APP_PROFILER_PSS_PROFILING:
updateDisableAppProfilerPssProfiling();
break;
+ case KEY_PSS_TO_RSS_THRESHOLD_MODIFIER:
+ updatePssToRssThresholdModifier();
+ break;
default:
updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
@@ -1339,6 +1352,10 @@ final class ActivityManagerConstants extends ContentObserver {
mDefaultDisableAppProfilerPssProfiling = context.getResources().getBoolean(
R.bool.config_am_disablePssProfiling);
APP_PROFILER_PSS_PROFILING_DISABLED = mDefaultDisableAppProfilerPssProfiling;
+
+ mDefaultPssToRssThresholdModifier = context.getResources().getFloat(
+ com.android.internal.R.dimen.config_am_pssToRssThresholdModifier);
+ PSS_TO_RSS_THRESHOLD_MODIFIER = mDefaultPssToRssThresholdModifier;
}
public void start(ContentResolver resolver) {
@@ -2051,6 +2068,12 @@ final class ActivityManagerConstants extends ContentObserver {
mDefaultDisableAppProfilerPssProfiling);
}
+ private void updatePssToRssThresholdModifier() {
+ PSS_TO_RSS_THRESHOLD_MODIFIER = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_PSS_TO_RSS_THRESHOLD_MODIFIER,
+ mDefaultPssToRssThresholdModifier);
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2242,6 +2265,9 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_DISABLE_APP_PROFILER_PSS_PROFILING);
pw.print("="); pw.println(APP_PROFILER_PSS_PROFILING_DISABLED);
+ pw.print(" "); pw.print(KEY_PSS_TO_RSS_THRESHOLD_MODIFIER);
+ pw.print("="); pw.println(PSS_TO_RSS_THRESHOLD_MODIFIER);
+
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 5dd0a3f58217..55b161ad6348 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -71,6 +71,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_PROCESSES = DEBUG_ALL || false;
static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
static final boolean DEBUG_PSS = DEBUG_ALL || false;
+ static final boolean DEBUG_RSS = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
@@ -91,6 +92,7 @@ class ActivityManagerDebugConfig {
? "_ProcessObservers" : "";
static final String POSTFIX_PROCESSES = (APPEND_CATEGORY_NAME) ? "_Processes" : "";
static final String POSTFIX_PSS = (APPEND_CATEGORY_NAME) ? "_Pss" : "";
+ static final String POSTFIX_RSS = (APPEND_CATEGORY_NAME) ? "_Rss" : "";
static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
static final String POSTFIX_SERVICE_EXECUTING =
(APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b99a98fe6e8b..c64fb2366dd6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -327,6 +327,7 @@ import android.os.Debug;
import android.os.DropBoxManager;
import android.os.FactoryTest;
import android.os.FileUtils;
+import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.IDeviceIdentifiersPolicyService;
@@ -599,6 +600,9 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
+ public static final String DATA_FILE_PATH_HEADER = "Data File: ";
+ public static final String DATA_FILE_PATH_FOOTER = "End Data File\n";
+
// If set, we will push process association information in to procstats.
static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
@@ -6560,7 +6564,7 @@ public class ActivityManagerService extends IActivityManager.Stub
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
final Intent intent = new Intent();
- intent.setData(uri);
+ intent.setData(ContentProvider.maybeAddUserId(uri, userId));
intent.setFlags(modeFlags);
final NeededUriGrants needed = mUgmInternal.checkGrantUriPermissionFromIntent(intent,
@@ -8560,6 +8564,12 @@ public class ActivityManagerService extends IActivityManager.Stub
// If the processes' memory has increased by more than 1% of the total memory,
// or 10 MB, whichever is greater, then the processes' are eligible to be killed.
final long totalMemoryInKb = getTotalMemory() / 1000;
+
+ // This threshold should be applicable to both PSS and RSS because the value is absolute
+ // and represents an increase in process memory relative to its own previous state.
+ //
+ // TODO(b/296454553): Tune this value during the flag rollout process if more processes
+ // seem to be getting killed than before.
final long memoryGrowthThreshold =
Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
mProcessList.forEachLruProcessesLOSP(false, proc -> {
@@ -8572,24 +8582,34 @@ public class ActivityManagerService extends IActivityManager.Stub
if (state.isNotCachedSinceIdle()) {
if (setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
- final long initialIdlePss, lastPss, lastSwapPss;
+ final long initialIdlePssOrRss, lastPssOrRss, lastSwapPss;
synchronized (mAppProfiler.mProfilerLock) {
- initialIdlePss = pr.getInitialIdlePss();
- lastPss = pr.getLastPss();
+ initialIdlePssOrRss = pr.getInitialIdlePssOrRss();
+ lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+ ? pr.getLastPss() : pr.getLastRss();
lastSwapPss = pr.getLastSwapPss();
}
- if (doKilling && initialIdlePss != 0
- && lastPss > (initialIdlePss * 3 / 2)
- && lastPss > (initialIdlePss + memoryGrowthThreshold)) {
+ if (doKilling && initialIdlePssOrRss != 0
+ && lastPssOrRss > (initialIdlePssOrRss * 3 / 2)
+ && lastPssOrRss > (initialIdlePssOrRss + memoryGrowthThreshold)) {
final StringBuilder sb2 = new StringBuilder(128);
sb2.append("Kill");
sb2.append(proc.processName);
- sb2.append(" in idle maint: pss=");
- sb2.append(lastPss);
- sb2.append(", swapPss=");
- sb2.append(lastSwapPss);
- sb2.append(", initialPss=");
- sb2.append(initialIdlePss);
+ if (!Flags.removeAppProfilerPssCollection()) {
+ sb2.append(" in idle maint: pss=");
+ } else {
+ sb2.append(" in idle maint: rss=");
+ }
+ sb2.append(lastPssOrRss);
+
+ if (!Flags.removeAppProfilerPssCollection()) {
+ sb2.append(", swapPss=");
+ sb2.append(lastSwapPss);
+ sb2.append(", initialPss=");
+ } else {
+ sb2.append(", initialRss=");
+ }
+ sb2.append(initialIdlePssOrRss);
sb2.append(", period=");
TimeUtils.formatDuration(timeSinceLastIdle, sb2);
sb2.append(", lowRamPeriod=");
@@ -8597,8 +8617,9 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.wtfQuiet(TAG, sb2.toString());
mHandler.post(() -> {
synchronized (ActivityManagerService.this) {
- proc.killLocked("idle maint (pss " + lastPss
- + " from " + initialIdlePss + ")",
+ proc.killLocked(!Flags.removeAppProfilerPssCollection()
+ ? "idle maint (pss " : "idle maint (rss " + lastPssOrRss
+ + " from " + initialIdlePssOrRss + ")",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
true);
@@ -8610,7 +8631,7 @@ public class ActivityManagerService extends IActivityManager.Stub
&& setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
state.setNotCachedSinceIdle(true);
synchronized (mAppProfiler.mProfilerLock) {
- pr.setInitialIdlePss(0);
+ pr.setInitialIdlePssOrRss(0);
mAppProfiler.updateNextPssTimeLPf(
state.getSetProcState(), proc.mProfile, now, true);
}
@@ -9364,6 +9385,8 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
void appendDropBoxProcessHeaders(ProcessRecord process, String processName,
final VolatileDropboxEntryStates volatileStates, final StringBuilder sb) {
+ sb.append("SystemUptimeMs: ").append(SystemClock.uptimeMillis()).append("\n");
+
// Watchdog thread ends up invoking this function (with
// a null ProcessRecord) to add the stack file to dropbox.
// Do not acquire a lock on this (am) in such cases, as it
@@ -9595,17 +9618,33 @@ public class ActivityManagerService extends IActivityManager.Stub
: Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
int dropboxMaxSize = Settings.Global.getInt(
mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
- int maxDataFileSize = dropboxMaxSize - sb.length()
- - lines * RESERVED_BYTES_PER_LOGCAT_LINE;
- if (dataFile != null && maxDataFileSize > 0) {
- try {
- sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
- "\n\n[[TRUNCATED]]"));
- } catch (IOException e) {
- Slog.e(TAG, "Error reading " + dataFile, e);
+ if (dataFile != null) {
+ // Attach the stack traces file to the report so collectors can load them
+ // by file if they have access.
+ sb.append(DATA_FILE_PATH_HEADER)
+ .append(dataFile.getAbsolutePath()).append('\n');
+
+ int maxDataFileSize = dropboxMaxSize
+ - sb.length()
+ - lines * RESERVED_BYTES_PER_LOGCAT_LINE
+ - DATA_FILE_PATH_FOOTER.length();
+
+ if (maxDataFileSize > 0) {
+ // Inline dataFile contents if there is room.
+ try {
+ sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
+ "\n\n[[TRUNCATED]]\n"));
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + dataFile, e);
+ }
}
+
+ // Always append the footer, even there wasn't enough space to inline the
+ // dataFile contents.
+ sb.append(DATA_FILE_PATH_FOOTER);
}
+
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a95ddf38e659..f3b2ef34ad6d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -552,7 +552,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
mAsync = true;
} else if (opt.equals("--splashscreen-show-icon")) {
mShowSplashScreen = true;
- } else if (opt.equals("--dismiss-keyguard-if-insecure")) {
+ } else if (opt.equals("--dismiss-keyguard-if-insecure")
+ || opt.equals("--dismiss-keyguard")) {
mDismissKeyguardIfInsecure = true;
} else {
return false;
@@ -4157,7 +4158,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" -D: enable debugging");
pw.println(" --suspend: debugged app suspend threads at startup (only with -D)");
pw.println(" -N: enable native debugging");
- pw.println(" -W: wait for launch to complete");
+ pw.println(" -W: wait for launch to complete (initial display)");
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples (use with --start-profiler)");
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3cf4332349d7..2e0aec973bd6 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -28,7 +28,9 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MOD
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RSS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.DUMP_MEM_OOM_ADJ;
@@ -64,6 +66,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
+import android.os.Flags;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -125,6 +128,7 @@ public class AppProfiler {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
static final String TAG_PSS = TAG + POSTFIX_PSS;
+ static final String TAG_RSS = TAG + POSTFIX_RSS;
static final String TAG_OOM_ADJ = ActivityManagerService.TAG_OOM_ADJ;
@@ -183,10 +187,10 @@ public class AppProfiler {
private volatile long mPssDeferralTime = 0;
/**
- * Processes we want to collect PSS data from.
+ * Processes we want to collect PSS or RSS data from.
*/
@GuardedBy("mProfilerLock")
- private final ArrayList<ProcessProfileRecord> mPendingPssProfiles = new ArrayList<>();
+ private final ArrayList<ProcessProfileRecord> mPendingPssOrRssProfiles = new ArrayList<>();
/**
* Depth of overlapping activity-start PSS deferral notes
@@ -200,18 +204,18 @@ public class AppProfiler {
private long mLastFullPssTime = SystemClock.uptimeMillis();
/**
- * If set, the next time we collect PSS data we should do a full collection
- * with data from native processes and the kernel.
+ * If set, the next time we collect PSS or RSS data we should do a full collection with data
+ * from native processes and the kernel.
*/
@GuardedBy("mProfilerLock")
- private boolean mFullPssPending = false;
+ private boolean mFullPssOrRssPending = false;
/**
- * If true, we are running under a test environment so will sample PSS from processes
- * much more rapidly to try to collect better data when the tests are rapidly
- * running through apps.
+ * If true, we are running under a test environment so will sample PSS or RSS from processes
+ * much more rapidly to try to collect better data when the tests are rapidly running through
+ * apps.
*/
- private volatile boolean mTestPssMode = false;
+ private volatile boolean mTestPssOrRssMode = false;
private final LowMemDetector mLowMemDetector;
@@ -598,7 +602,11 @@ public class AppProfiler {
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG:
- collectPssInBackground();
+ if (!Flags.removeAppProfilerPssCollection()) {
+ collectPssInBackground();
+ } else {
+ collectRssInBackground();
+ }
break;
case DEFER_PSS_MSG:
deferPssForActivityStart();
@@ -619,8 +627,8 @@ public class AppProfiler {
long start = SystemClock.uptimeMillis();
MemInfoReader memInfo = null;
synchronized (mProfilerLock) {
- if (mFullPssPending) {
- mFullPssPending = false;
+ if (mFullPssOrRssPending) {
+ mFullPssOrRssPending = false;
memInfo = new MemInfoReader();
}
}
@@ -673,16 +681,16 @@ public class AppProfiler {
int pid = -1;
long lastPssTime;
synchronized (mProfilerLock) {
- if (mPendingPssProfiles.size() <= 0) {
- if (mTestPssMode || DEBUG_PSS) {
+ if (mPendingPssOrRssProfiles.size() <= 0) {
+ if (mTestPssOrRssMode || DEBUG_PSS) {
Slog.d(TAG_PSS,
"Collected pss of " + num + " processes in "
+ (SystemClock.uptimeMillis() - start) + "ms");
}
- mPendingPssProfiles.clear();
+ mPendingPssOrRssProfiles.clear();
return;
}
- profile = mPendingPssProfiles.remove(0);
+ profile = mPendingPssOrRssProfiles.remove(0);
procState = profile.getPssProcState();
statType = profile.getPssStatType();
lastPssTime = profile.getLastPssTime();
@@ -740,13 +748,149 @@ public class AppProfiler {
} while (true);
}
+ // This method is analogous to collectPssInBackground() and is intended to be used as a
+ // replacement if Flags.removeAppProfilerPssCollection() is enabled. References to PSS in
+ // methods outside of AppProfiler have generally been kept where a new RSS equivalent is not
+ // technically necessary. These can be updated once the flag is completely rolled out.
+ private void collectRssInBackground() {
+ long start = SystemClock.uptimeMillis();
+ MemInfoReader memInfo = null;
+ synchronized (mProfilerLock) {
+ if (mFullPssOrRssPending) {
+ mFullPssOrRssPending = false;
+ memInfo = new MemInfoReader();
+ }
+ }
+ if (memInfo != null) {
+ updateCpuStatsNow();
+ long nativeTotalRss = 0;
+ final List<ProcessCpuTracker.Stats> stats;
+ synchronized (mProcessCpuTracker) {
+ stats = mProcessCpuTracker.getStats(st -> {
+ return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
+ });
+ }
+
+ // We assume that if PSS collection isn't needed or desired, RSS collection can be
+ // disabled as well.
+ if (!mService.mConstants.APP_PROFILER_PSS_PROFILING_DISABLED) {
+ final int numOfStats = stats.size();
+ for (int j = 0; j < numOfStats; j++) {
+ synchronized (mService.mPidsSelfLocked) {
+ if (mService.mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
+ // This is one of our own processes; skip it.
+ continue;
+ }
+ }
+ nativeTotalRss += Debug.getRss(stats.get(j).pid, null);
+ }
+ }
+
+ memInfo.readMemInfo();
+ synchronized (mService.mProcessStats.mLock) {
+ // We assume that an enabled DEBUG_PSS can apply to RSS as well, since only one of
+ // either collectPssInBackground() or collectRssInBackground() will be used.
+ if (DEBUG_RSS) {
+ Slog.d(TAG_RSS, "Collected native and kernel memory in "
+ + (SystemClock.uptimeMillis() - start) + "ms");
+ }
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ // The last value needs to be updated in log tags to refer to RSS; this will be
+ // updated once the flag is fully rolled out.
+ EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+ kernelKb * 1024, nativeTotalRss * 1024);
+ mService.mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeTotalRss);
+ }
+ }
+
+ // This loop differs from its original form in collectPssInBackground(), as it does not
+ // collect USS or SwapPss (since those are reported in smaps, not status).
+ int num = 0;
+ do {
+ ProcessProfileRecord profile;
+ int procState;
+ int statType;
+ int pid = -1;
+ long lastRssTime;
+ synchronized (mProfilerLock) {
+ if (mPendingPssOrRssProfiles.size() <= 0) {
+ if (mTestPssOrRssMode || DEBUG_RSS) {
+ Slog.d(TAG_RSS,
+ "Collected rss of " + num + " processes in "
+ + (SystemClock.uptimeMillis() - start) + "ms");
+ }
+ mPendingPssOrRssProfiles.clear();
+ return;
+ }
+ profile = mPendingPssOrRssProfiles.remove(0);
+ procState = profile.getPssProcState();
+ statType = profile.getPssStatType();
+ lastRssTime = profile.getLastPssTime();
+ long now = SystemClock.uptimeMillis();
+ if (profile.getThread() != null && procState == profile.getSetProcState()
+ && (lastRssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) {
+ pid = profile.getPid();
+ } else {
+ profile.abortNextPssTime();
+ if (DEBUG_RSS) {
+ Slog.d(TAG_RSS, "Skipped rss collection of " + pid
+ + ": still need "
+ + (lastRssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now)
+ + "ms until safe");
+ }
+ profile = null;
+ pid = 0;
+ }
+ }
+ if (profile != null) {
+ long startTime = SystemClock.currentThreadTimeMillis();
+ // skip background RSS calculation under the following situations:
+ // - app is capturing camera imagery
+ // - app is frozen and we have already collected RSS once.
+ final boolean skipRSSCollection =
+ (profile.mApp.mOptRecord != null
+ && profile.mApp.mOptRecord.skipPSSCollectionBecauseFrozen())
+ || mService.isCameraActiveForUid(profile.mApp.uid)
+ || mService.mConstants.APP_PROFILER_PSS_PROFILING_DISABLED;
+ long rss = skipRSSCollection ? 0 : Debug.getRss(pid, null);
+ long endTime = SystemClock.currentThreadTimeMillis();
+ synchronized (mProfilerLock) {
+ if (rss != 0 && profile.getThread() != null
+ && profile.getSetProcState() == procState
+ && profile.getPid() == pid && profile.getLastPssTime() == lastRssTime) {
+ num++;
+ profile.commitNextPssTime();
+ recordRssSampleLPf(profile, procState, rss, statType, endTime - startTime,
+ SystemClock.uptimeMillis());
+ } else {
+ profile.abortNextPssTime();
+ if (DEBUG_RSS) {
+ Slog.d(TAG_RSS, "Skipped rss collection of " + pid
+ + ": " + (profile.getThread() == null ? "NO_THREAD " : "")
+ + (skipRSSCollection ? "SKIP_RSS_COLLECTION " : "")
+ + (profile.getPid() != pid ? "PID_CHANGED " : "")
+ + " initState=" + procState + " curState="
+ + profile.getSetProcState() + " "
+ + (profile.getLastPssTime() != lastRssTime
+ ? "TIME_CHANGED" : ""));
+ }
+ }
+ }
+ }
+ } while (true);
+ }
+
@GuardedBy("mProfilerLock")
void updateNextPssTimeLPf(int procState, ProcessProfileRecord profile, long now,
boolean forceUpdate) {
if (!forceUpdate) {
if (now <= profile.getNextPssTime() && now <= Math.max(profile.getLastPssTime()
+ ProcessList.PSS_MAX_INTERVAL, profile.getLastStateTime()
- + ProcessList.minTimeFromStateChange(mTestPssMode))) {
+ + ProcessList.minTimeFromStateChange(mTestPssOrRssMode))) {
// update is not due, ignore it.
return;
}
@@ -755,7 +899,7 @@ public class AppProfiler {
}
}
profile.setNextPssTime(profile.computeNextPssTime(procState,
- mTestPssMode, mService.mAtmInternal.isSleeping(), now));
+ mTestPssOrRssMode, mService.mAtmInternal.isSleeping(), now));
}
/**
@@ -776,8 +920,8 @@ public class AppProfiler {
+ " lastPss=" + profile.getLastPss()
+ " state=" + ProcessList.makeProcStateString(procState));
}
- if (profile.getInitialIdlePss() == 0) {
- profile.setInitialIdlePss(pss);
+ if (profile.getInitialIdlePssOrRss() == 0) {
+ profile.setInitialIdlePssOrRss(pss);
}
profile.setLastPss(pss);
profile.setLastSwapPss(swapPss);
@@ -813,6 +957,72 @@ public class AppProfiler {
}
}
+ /**
+ * Record new RSS sample for a process.
+ *
+ * This method is analogous to recordPssSampleLPf() and is intended to be used as a replacement
+ * if Flags.removeAppProfilerPssCollection() is enabled. Functionally, this differs in that PSS,
+ * SwapPss, and USS are no longer collected and reported.
+ *
+ * This method will also poll PSS if the app has requested that a heap dump be taken if its PSS
+ * reaches some threshold set with ActivityManager.setWatchHeapLimit().
+ */
+ @GuardedBy("mProfilerLock")
+ private void recordRssSampleLPf(ProcessProfileRecord profile, int procState, long rss,
+ int statType, long rssDuration, long now) {
+ final ProcessRecord proc = profile.mApp;
+ // TODO(b/296454553): writeAmPss needs to be renamed to writeAmRss, and the zeroed out
+ // fields need to be removed. This will be updated once the flag is fully rolled out to
+ // avoid churn in the .logtags file, which has a mapping of IDs to tags (and is also
+ // technically deprecated).
+ EventLogTags.writeAmPss(
+ profile.getPid(), proc.uid, proc.processName, /* pss = */ 0, /* uss = */ 0,
+ /* swapPss = */ 0, rss * 1024, statType, procState, rssDuration);
+ profile.setLastPssTime(now);
+ // The PSS here is emitted in logs, so we can zero it out instead of subbing in RSS.
+ profile.addPss(/* pss = */ 0, /* uss = */ 0, rss, true, statType, rssDuration);
+ if (DEBUG_RSS) {
+ Slog.d(TAG_RSS,
+ "rss of " + proc.toShortString() + ": " + rss
+ + " lastRss=" + profile.getLastRss()
+ + " state=" + ProcessList.makeProcStateString(procState));
+ }
+ if (profile.getInitialIdlePssOrRss() == 0) {
+ profile.setInitialIdlePssOrRss(rss);
+ }
+ profile.setLastRss(rss);
+ if (procState >= ActivityManager.PROCESS_STATE_HOME) {
+ profile.setLastCachedRss(rss);
+ }
+
+ final SparseArray<Pair<Long, String>> watchUids =
+ mMemWatchProcesses.getMap().get(proc.processName);
+ Long check = null;
+ if (watchUids != null) {
+ Pair<Long, String> val = watchUids.get(proc.uid);
+ if (val == null) {
+ val = watchUids.get(0);
+ }
+ if (val != null) {
+ check = val.first;
+ }
+ }
+
+ if (check != null) {
+ long pss = Debug.getPss(profile.getPid(), null, null);
+ if ((pss * 1024) >= check && profile.getThread() != null
+ && mMemWatchDumpProcName == null) {
+ if (Build.IS_DEBUGGABLE || proc.isDebuggable()) {
+ Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
+ startHeapDumpLPf(profile, false);
+ } else {
+ Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
+ + ", but debugging not enabled");
+ }
+ }
+ }
+ }
+
private final class RecordPssRunnable implements Runnable {
private final ProcessProfileRecord mProfile;
private final Uri mDumpUri;
@@ -984,10 +1194,10 @@ public class AppProfiler {
*/
@GuardedBy("mProfilerLock")
private boolean requestPssLPf(ProcessProfileRecord profile, int procState) {
- if (mPendingPssProfiles.contains(profile)) {
+ if (mPendingPssOrRssProfiles.contains(profile)) {
return false;
}
- if (mPendingPssProfiles.size() == 0) {
+ if (mPendingPssOrRssProfiles.size() == 0) {
final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
? mPssDeferralTime : 0;
if (DEBUG_PSS && deferral > 0) {
@@ -999,7 +1209,7 @@ public class AppProfiler {
if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + profile.mApp);
profile.setPssProcState(procState);
profile.setPssStatType(ProcessStats.ADD_PSS_INTERNAL_SINGLE);
- mPendingPssProfiles.add(profile);
+ mPendingPssOrRssProfiles.add(profile);
return true;
}
@@ -1009,7 +1219,7 @@ public class AppProfiler {
*/
@GuardedBy("mProfilerLock")
private void deferPssIfNeededLPf() {
- if (mPendingPssProfiles.size() > 0) {
+ if (mPendingPssOrRssProfiles.size() > 0) {
mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG);
mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime);
}
@@ -1063,12 +1273,12 @@ public class AppProfiler {
Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
}
mLastFullPssTime = now;
- mFullPssPending = true;
- for (int i = mPendingPssProfiles.size() - 1; i >= 0; i--) {
- mPendingPssProfiles.get(i).abortNextPssTime();
+ mFullPssOrRssPending = true;
+ for (int i = mPendingPssOrRssProfiles.size() - 1; i >= 0; i--) {
+ mPendingPssOrRssProfiles.get(i).abortNextPssTime();
}
- mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLOSP());
- mPendingPssProfiles.clear();
+ mPendingPssOrRssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLOSP());
+ mPendingPssOrRssProfiles.clear();
mService.mProcessList.forEachLruProcessesLOSP(false, app -> {
final ProcessProfileRecord profile = app.mProfile;
if (profile.getThread() == null
@@ -1083,7 +1293,7 @@ public class AppProfiler {
profile.setPssStatType(always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM);
updateNextPssTimeLPf(profile.getSetProcState(), profile, now, true);
- mPendingPssProfiles.add(profile);
+ mPendingPssOrRssProfiles.add(profile);
}
});
if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
@@ -1094,7 +1304,7 @@ public class AppProfiler {
void setTestPssMode(boolean enabled) {
synchronized (mProcLock) {
- mTestPssMode = enabled;
+ mTestPssOrRssMode = enabled;
if (enabled) {
// Whenever we enable the mode, we want to take a snapshot all of current
// process mem use.
@@ -1104,7 +1314,7 @@ public class AppProfiler {
}
boolean getTestPssMode() {
- return mTestPssMode;
+ return mTestPssOrRssMode;
}
@GuardedBy("mService")
@@ -2346,7 +2556,7 @@ public class AppProfiler {
synchronized (mProfilerLock) {
final ProcessProfileRecord profile = app.mProfile;
mProcessesToGc.remove(app);
- mPendingPssProfiles.remove(profile);
+ mPendingPssOrRssProfiles.remove(profile);
profile.abortNextPssTime();
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f9fc4d4f27fa..36356bd95128 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -111,6 +111,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -475,7 +476,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- nms.registerObserver(mActivityChangeObserver);
+ if (!SdkLevel.isAtLeastV()) {
+ // On V+ devices, ConnectivityService calls BatteryStats API to update
+ // RadioPowerState change. So BatteryStatsService registers the callback only on
+ // pre V devices.
+ nms.registerObserver(mActivityChangeObserver);
+ }
cm.registerDefaultNetworkCallback(mNetworkCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index e41b6ae28e80..3ce92bc2737e 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -265,13 +265,6 @@ class BroadcastProcessQueue {
@Nullable
public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
int recordIndex, @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
- // When updateDeferredStates() has already applied a deferred state to
- // all pending items, apply to this new broadcast too
- if (mLastDeferredStates && record.deferUntilActive
- && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
- deferredStatesApplyConsumer.accept(record, recordIndex);
- }
-
// Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the
// BroadcastOptions delivery group APIs.
if (record.isReplacePending()
@@ -294,6 +287,13 @@ class BroadcastProcessQueue {
// with implicit responsiveness expectations.
getQueueForBroadcast(record).addLast(newBroadcastArgs);
onBroadcastEnqueued(record, recordIndex);
+
+ // When updateDeferredStates() has already applied a deferred state to
+ // all pending items, apply to this new broadcast too
+ if (mLastDeferredStates && shouldBeDeferred()
+ && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
+ deferredStatesApplyConsumer.accept(record, recordIndex);
+ }
return null;
}
@@ -1235,32 +1235,45 @@ class BroadcastProcessQueue {
}
/**
- * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our
+ * Update {@link BroadcastRecord#DELIVERY_DEFERRED} states of all our
* pending broadcasts, when needed.
*/
void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer,
@NonNull BroadcastConsumer clearConsumer) {
// When all we have pending is deferred broadcasts, and we're cached,
// then we want everything to be marked deferred
- final boolean wantDeferredStates = (mCountDeferred > 0)
- && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+ final boolean wantDeferredStates = shouldBeDeferred();
if (mLastDeferredStates != wantDeferredStates) {
mLastDeferredStates = wantDeferredStates;
if (wantDeferredStates) {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
}, applyConsumer, false);
} else {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
}, clearConsumer, false);
}
}
}
+ void clearDeferredStates(@NonNull BroadcastConsumer clearConsumer) {
+ if (mLastDeferredStates) {
+ mLastDeferredStates = false;
+ forEachMatchingBroadcast((r, i) -> {
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ }, clearConsumer, false);
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldBeDeferred() {
+ if (mRunnableAtInvalidated) updateRunnableAt();
+ return mRunnableAtReason == REASON_CACHED
+ || mRunnableAtReason == REASON_CACHED_INFINITE_DEFER;
+ }
+
/**
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b48169788180..5b54561c2164 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -479,6 +479,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
break;
}
+ // Clear the deferred state of broadcasts in this queue as we are just about to
+ // deliver broadcasts to this process.
+ queue.clearDeferredStates(mBroadcastConsumerDeferClear);
+
// We might not have heard about a newly running process yet, so
// consider refreshing if we think we're cold
updateWarmProcess(queue);
@@ -1567,12 +1571,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
r.resultExtras = null;
};
- private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
"mBroadcastConsumerDeferApply");
};
- private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
"mBroadcastConsumerDeferClear");
};
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index a0a7b2b48725..d0ab287785e3 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1472,10 +1472,13 @@ public final class CachedAppOptimizer {
}
return;
}
+ boolean processFreezableChangeReported = false;
if (opt.isPendingFreeze()) {
// Remove pending DO_FREEZE message
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
opt.setPendingFreeze(false);
+ reportProcessFreezableChangedLocked(app);
+ processFreezableChangeReported = true;
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Cancel freezing " + pid + " " + app.processName);
}
@@ -1524,7 +1527,9 @@ public final class CachedAppOptimizer {
if (processKilled) {
return;
}
- reportProcessFreezableChangedLocked(app);
+ if (!processFreezableChangeReported) {
+ reportProcessFreezableChangedLocked(app);
+ }
long freezeTime = opt.getFreezeUnfreezeTime();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9bba08ad1bfd..b00dcd6ccf1f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -144,6 +144,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.net.NetworkPolicyManager;
+import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
@@ -1829,7 +1830,7 @@ public class OomAdjuster {
// screen on or animating, promote UI
state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP);
- } else {
+ } else if (!app.getWindowProcessController().isShowingUiWhileDozing()) {
// screen off, restrict UI scheduling
state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
state.setCurrentSchedulingGroup(SCHED_GROUP_RESTRICTED);
@@ -2230,8 +2231,10 @@ public class OomAdjuster {
|| now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
// recent memory, so we will keep its process ahead
- // of the background processes.
- if (adj > SERVICE_ADJ) {
+ // of the background processes. This does not apply
+ // to the SDK sandbox process since it should never
+ // be more important than its corresponding app.
+ if (!app.isSdkSandbox && adj > SERVICE_ADJ) {
adj = SERVICE_ADJ;
state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -2416,9 +2419,23 @@ public class OomAdjuster {
// normally be a B service, but if we are low on RAM and it
// is large we want to force it down since we would prefer to
// keep launcher over it.
+ long lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+ ? app.mProfile.getLastPss() : app.mProfile.getLastRss();
+
+ // RSS is larger than PSS, but the RSS/PSS ratio varies per-process based on how
+ // many shared pages a process uses. The threshold is increased if the flag for
+ // reading RSS instead of PSS is enabled.
+ //
+ // TODO(b/296454553): Tune the second value so that the relative number of
+ // service B is similar before/after this flag is enabled.
+ double thresholdModifier = !Flags.removeAppProfilerPssCollection()
+ ? 1
+ : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
+ double cachedRestoreThreshold =
+ mProcessList.getCachedRestoreThresholdKb() * thresholdModifier;
+
if (!mService.mAppProfiler.isLastMemoryLevelNormal()
- && app.mProfile.getLastPss()
- >= mProcessList.getCachedRestoreThresholdKb()) {
+ && lastPssOrRss >= cachedRestoreThreshold) {
state.setServiceHighRam(true);
state.setServiceB(true);
//Slog.i(TAG, "ADJ " + app + " high ram!");
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index b852ef56fceb..d372108e0a47 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -67,10 +67,8 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.content.pm.ServiceInfo;
-import android.os.IBinder;
import android.os.SystemClock;
import android.os.Trace;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -80,7 +78,6 @@ import com.android.server.ServiceThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -504,6 +501,28 @@ public class OomAdjusterModernImpl extends OomAdjuster {
}
}
+ /**
+ * A helper consumer for collecting processes that have not been reached yet. To avoid object
+ * allocations every OomAdjuster update, the results will be stored in
+ * {@link UnreachedProcessCollector#processList}. The process list reader is responsible
+ * for setting it before usage, as well as, clearing the reachable state of each process in the
+ * list.
+ */
+ private static class UnreachedProcessCollector implements Consumer<ProcessRecord> {
+ public ArrayList<ProcessRecord> processList = null;
+ @Override
+ public void accept(ProcessRecord process) {
+ if (process.mState.isReachable()) {
+ return;
+ }
+ process.mState.setReachable(true);
+ processList.add(process);
+ }
+ }
+
+ private final UnreachedProcessCollector mUnreachedProcessCollector =
+ new UnreachedProcessCollector();
+
OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
ActiveUids activeUids) {
this(service, processList, activeUids, createAdjusterThread());
@@ -755,23 +774,8 @@ public class OomAdjusterModernImpl extends OomAdjuster {
// We'll need to collect the upstream processes of the target apps here, because those
// processes would potentially impact the procstate/adj via bindings.
if (!fullUpdate) {
- final boolean containsCycle = collectReversedReachableProcessesLocked(targetProcesses,
- clientProcesses);
-
- // If any of its upstream processes are in a cycle,
- // move them into the candidate targets.
- if (containsCycle) {
- // Add all client apps to the target process list.
- for (int i = 0, size = clientProcesses.size(); i < size; i++) {
- final ProcessRecord client = clientProcesses.get(i);
- final UidRecord uidRec = client.getUidRecord();
- targetProcesses.add(client);
- if (uidRec != null) {
- uids.put(uidRec.getUid(), uidRec);
- }
- }
- clientProcesses.clear();
- }
+ collectExcludedClientProcessesLocked(targetProcesses, clientProcesses);
+
for (int i = 0, size = targetProcesses.size(); i < size; i++) {
final ProcessRecord app = targetProcesses.valueAt(i);
app.mState.resetCachedInfo();
@@ -807,102 +811,36 @@ public class OomAdjusterModernImpl extends OomAdjuster {
}
/**
- * Collect the reversed reachable processes from the given {@code apps}, the result will be
- * returned in the given {@code processes}, which will <em>NOT</em> include the processes from
- * the given {@code apps}.
+ * Collect the client processes from the given {@code apps}, the result will be returned in the
+ * given {@code clientProcesses}, which will <em>NOT</em> include the processes from the given
+ * {@code apps}.
*/
@GuardedBy("mService")
- private boolean collectReversedReachableProcessesLocked(ArraySet<ProcessRecord> apps,
+ private void collectExcludedClientProcessesLocked(ArraySet<ProcessRecord> apps,
ArrayList<ProcessRecord> clientProcesses) {
- final ArrayDeque<ProcessRecord> queue = mTmpQueue;
- queue.clear();
- clientProcesses.clear();
- for (int i = 0, size = apps.size(); i < size; i++) {
+ // Mark all of the provided apps as reachable to avoid including them in the client list.
+ final int appsSize = apps.size();
+ for (int i = 0; i < appsSize; i++) {
final ProcessRecord app = apps.valueAt(i);
app.mState.setReachable(true);
- app.mState.setReversedReachable(true);
- queue.offer(app);
}
- // Track if any of them reachables could include a cycle
- boolean containsCycle = false;
-
- // Scan upstreams of the process record
- for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) {
- if (!pr.mState.isReachable()) {
- // If not in the given initial set of apps, add it.
- clientProcesses.add(pr);
- }
- final ProcessServiceRecord psr = pr.mServices;
- for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
- final ServiceRecord s = psr.getRunningServiceAt(i);
- final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
- s.getConnections();
- for (int j = serviceConnections.size() - 1; j >= 0; j--) {
- final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
- for (int k = clist.size() - 1; k >= 0; k--) {
- final ConnectionRecord cr = clist.get(k);
- final ProcessRecord client = cr.binding.client;
- containsCycle |= client.mState.isReversedReachable();
- if (client.mState.isReversedReachable()) {
- continue;
- }
- queue.offer(client);
- client.mState.setReversedReachable(true);
- }
- }
- }
- final ProcessProviderRecord ppr = pr.mProviders;
- for (int i = ppr.numberOfProviders() - 1; i >= 0; i--) {
- final ContentProviderRecord cpr = ppr.getProviderAt(i);
- for (int j = cpr.connections.size() - 1; j >= 0; j--) {
- final ContentProviderConnection conn = cpr.connections.get(j);
- final ProcessRecord client = conn.client;
- containsCycle |= client.mState.isReversedReachable();
- if (client.mState.isReversedReachable()) {
- continue;
- }
- queue.offer(client);
- client.mState.setReversedReachable(true);
- }
- }
- // If this process is a sandbox itself, also add the app on whose behalf
- // its running
- if (pr.isSdkSandbox) {
- for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
- ServiceRecord s = psr.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 == pr) {
- continue;
- }
- containsCycle |= attributedApp.mState.isReversedReachable();
- if (attributedApp.mState.isReversedReachable()) {
- continue;
- }
- queue.offer(attributedApp);
- attributedApp.mState.setReversedReachable(true);
- }
- }
- }
- }
+ clientProcesses.clear();
+ mUnreachedProcessCollector.processList = clientProcesses;
+ for (int i = 0; i < appsSize; i++) {
+ final ProcessRecord app = apps.valueAt(i);
+ app.forEachClient(mUnreachedProcessCollector);
}
+ mUnreachedProcessCollector.processList = null;
// Reset the temporary bits.
for (int i = clientProcesses.size() - 1; i >= 0; i--) {
- clientProcesses.get(i).mState.setReversedReachable(false);
+ clientProcesses.get(i).mState.setReachable(false);
}
for (int i = 0, size = apps.size(); i < size; i++) {
final ProcessRecord app = apps.valueAt(i);
app.mState.setReachable(false);
- app.mState.setReversedReachable(false);
}
- return containsCycle;
}
@GuardedBy({"mService", "mProcLock"})
@@ -917,10 +855,6 @@ public class OomAdjusterModernImpl extends OomAdjuster {
final int procStateTarget = mProcessRecordProcStateNodes.size() - 1;
final int adjTarget = mProcessRecordAdjNodes.size() - 1;
- final int appUid = !fullUpdate && targetProcesses.size() > 0
- ? targetProcesses.valueAt(0).uid : -1;
- final int logUid = mService.mCurOomAdjUid;
-
mAdjSeq++;
// All apps to be updated will be moved to the lowest slot.
if (fullUpdate) {
@@ -974,7 +908,7 @@ public class OomAdjusterModernImpl extends OomAdjuster {
// We don't update the adj list since we're resetting it below.
}
- // Now nodes are set into their slots, without facting in the bindings.
+ // Now nodes are set into their slots, without factoring in the bindings.
// The nodes between the `lastNode` pointer and the TAIL should be the new nodes.
//
// The whole rationale here is that, the bindings from client to host app, won't elevate
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2efac12a53a2..cb2b5fbe7a5a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -94,6 +94,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
+import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -4594,6 +4595,8 @@ public final class ProcessList {
r.mProfile.getLastPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastSwapPss() * 1024, new StringBuilder()));
+ // TODO(b/296454553): This proto field should be replaced with last cached RSS once
+ // AppProfiler is no longer collecting PSS.
proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastCachedPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.CACHED, state.isCached());
@@ -4725,12 +4728,20 @@ public final class ProcessList {
pw.print(" ");
pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
- pw.print(" lastPss=");
- DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
- pw.print(" lastSwapPss=");
- DebugUtils.printSizeValue(pw, r.mProfile.getLastSwapPss() * 1024);
- pw.print(" lastCachedPss=");
- DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedPss() * 1024);
+ // These values won't be collected if the flag is enabled.
+ if (!Flags.removeAppProfilerPssCollection()) {
+ pw.print(" lastPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
+ pw.print(" lastSwapPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastSwapPss() * 1024);
+ pw.print(" lastCachedPss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedPss() * 1024);
+ } else {
+ pw.print(" lastRss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastRss() * 1024);
+ pw.print(" lastCachedRss=");
+ DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedRss() * 1024);
+ }
pw.println();
pw.print(prefix);
pw.print(" ");
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 354f3d3d13e0..8ca64f83ea79 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -23,6 +23,7 @@ import android.app.IApplicationThread;
import android.app.ProcessMemoryState.HostingComponentType;
import android.content.pm.ApplicationInfo;
import android.os.Debug;
+import android.os.Flags;
import android.os.Process;
import android.os.SystemClock;
import android.util.DebugUtils;
@@ -32,7 +33,6 @@ import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.ProcessList.ProcStateMemTracker;
import com.android.server.power.stats.BatteryStatsImpl;
@@ -42,6 +42,8 @@ import java.util.concurrent.atomic.AtomicLong;
/**
* Profiling info of the process, such as PSS, cpu, etc.
+ *
+ * TODO(b/297542292): Update PSS names with RSS once AppProfiler's PSS profiling has been replaced.
*/
final class ProcessProfileRecord {
final ProcessRecord mApp;
@@ -76,7 +78,7 @@ final class ProcessProfileRecord {
* Initial memory pss of process for idle maintenance.
*/
@GuardedBy("mProfilerLock")
- private long mInitialIdlePss;
+ private long mInitialIdlePssOrRss;
/**
* Last computed memory pss.
@@ -109,6 +111,14 @@ final class ProcessProfileRecord {
private long mLastRss;
/**
+ * Last computed rss when in cached state.
+ *
+ * This value is not set or retrieved unless Flags.removeAppProfilerPssCollection() is true.
+ */
+ @GuardedBy("mProfilerLock")
+ private long mLastCachedRss;
+
+ /**
* Cache of last retrieve memory info, to throttle how frequently apps can request it.
*/
@GuardedBy("mProfilerLock")
@@ -347,13 +357,13 @@ final class ProcessProfileRecord {
}
@GuardedBy("mProfilerLock")
- long getInitialIdlePss() {
- return mInitialIdlePss;
+ long getInitialIdlePssOrRss() {
+ return mInitialIdlePssOrRss;
}
@GuardedBy("mProfilerLock")
- void setInitialIdlePss(long initialIdlePss) {
- mInitialIdlePss = initialIdlePss;
+ void setInitialIdlePssOrRss(long initialIdlePssOrRss) {
+ mInitialIdlePssOrRss = initialIdlePssOrRss;
}
@GuardedBy("mProfilerLock")
@@ -377,6 +387,16 @@ final class ProcessProfileRecord {
}
@GuardedBy("mProfilerLock")
+ long getLastCachedRss() {
+ return mLastCachedRss;
+ }
+
+ @GuardedBy("mProfilerLock")
+ void setLastCachedRss(long lastCachedRss) {
+ mLastCachedRss = lastCachedRss;
+ }
+
+ @GuardedBy("mProfilerLock")
long getLastSwapPss() {
return mLastSwapPss;
}
@@ -530,26 +550,6 @@ final class ProcessProfileRecord {
}
}
- void reportCachedKill() {
- synchronized (mService.mProcessStats.mLock) {
- final ProcessState tracker = mBaseProcessTracker;
- if (tracker != null) {
- final PackageList pkgList = mApp.getPkgList();
- synchronized (pkgList) {
- tracker.reportCachedKill(pkgList.getPackageListLocked(), mLastCachedPss);
- pkgList.forEachPackageProcessStats(holder ->
- FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
- getUidForAttribution(mApp),
- holder.state.getName(),
- holder.state.getPackage(),
- mLastCachedPss,
- holder.appVersion)
- );
- }
- }
- }
- }
-
void setProcessTrackerState(int procState, int memFactor) {
synchronized (mService.mProcessStats.mLock) {
final ProcessState tracker = mBaseProcessTracker;
@@ -676,27 +676,46 @@ final class ProcessProfileRecord {
@GuardedBy("mService")
void dumpPss(PrintWriter pw, String prefix, long nowUptime) {
synchronized (mProfilerLock) {
- pw.print(prefix);
- pw.print("lastPssTime=");
- TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
- pw.print(" pssProcState=");
- pw.print(mPssProcState);
- pw.print(" pssStatType=");
- pw.print(mPssStatType);
- pw.print(" nextPssTime=");
- TimeUtils.formatDuration(mNextPssTime, nowUptime, pw);
- pw.println();
- pw.print(prefix);
- pw.print("lastPss=");
- DebugUtils.printSizeValue(pw, mLastPss * 1024);
- pw.print(" lastSwapPss=");
- DebugUtils.printSizeValue(pw, mLastSwapPss * 1024);
- pw.print(" lastCachedPss=");
- DebugUtils.printSizeValue(pw, mLastCachedPss * 1024);
- pw.print(" lastCachedSwapPss=");
- DebugUtils.printSizeValue(pw, mLastCachedSwapPss * 1024);
- pw.print(" lastRss=");
- DebugUtils.printSizeValue(pw, mLastRss * 1024);
+ // TODO(b/297542292): Remove this case once PSS profiling is replaced
+ if (!Flags.removeAppProfilerPssCollection()) {
+ pw.print(prefix);
+ pw.print("lastPssTime=");
+ TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
+ pw.print(" pssProcState=");
+ pw.print(mPssProcState);
+ pw.print(" pssStatType=");
+ pw.print(mPssStatType);
+ pw.print(" nextPssTime=");
+ TimeUtils.formatDuration(mNextPssTime, nowUptime, pw);
+ pw.println();
+ pw.print(prefix);
+ pw.print("lastPss=");
+ DebugUtils.printSizeValue(pw, mLastPss * 1024);
+ pw.print(" lastSwapPss=");
+ DebugUtils.printSizeValue(pw, mLastSwapPss * 1024);
+ pw.print(" lastCachedPss=");
+ DebugUtils.printSizeValue(pw, mLastCachedPss * 1024);
+ pw.print(" lastCachedSwapPss=");
+ DebugUtils.printSizeValue(pw, mLastCachedSwapPss * 1024);
+ pw.print(" lastRss=");
+ DebugUtils.printSizeValue(pw, mLastRss * 1024);
+ } else {
+ pw.print(prefix);
+ pw.print("lastRssTime=");
+ TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
+ pw.print(" rssProcState=");
+ pw.print(mPssProcState);
+ pw.print(" rssStatType=");
+ pw.print(mPssStatType);
+ pw.print(" nextRssTime=");
+ TimeUtils.formatDuration(mNextPssTime, nowUptime, pw);
+ pw.println();
+ pw.print(prefix);
+ pw.print("lastRss=");
+ DebugUtils.printSizeValue(pw, mLastRss * 1024);
+ pw.print(" lastCachedRss=");
+ DebugUtils.printSizeValue(pw, mLastCachedRss * 1024);
+ }
pw.println();
pw.print(prefix);
pw.print("trimMemoryLevel=");
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2c6e598ef0a4..b2082d9e8dc0 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -69,8 +69,10 @@ import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
/**
* Full information about a particular process that
@@ -1613,4 +1615,50 @@ class ProcessRecord implements WindowProcessListener {
public boolean wasForceStopped() {
return mWasForceStopped;
}
+
+ /**
+ * Traverses all client processes and feed them to consumer.
+ */
+ @GuardedBy("mProcLock")
+ void forEachClient(@NonNull Consumer<ProcessRecord> consumer) {
+ for (int i = mServices.numberOfRunningServices() - 1; i >= 0; i--) {
+ final ServiceRecord s = mServices.getRunningServiceAt(i);
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+ s.getConnections();
+ for (int j = serviceConnections.size() - 1; j >= 0; j--) {
+ 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);
+ }
+ }
+ }
+ for (int i = mProviders.numberOfProviders() - 1; i >= 0; i--) {
+ final ContentProviderRecord cpr = mProviders.getProviderAt(i);
+ for (int j = cpr.connections.size() - 1; j >= 0; j--) {
+ final ContentProviderConnection conn = cpr.connections.get(j);
+ 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/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 27c08763fab0..5ad921fd0bae 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -29,6 +29,7 @@ import static com.android.server.am.ProcessRecord.TAG;
import android.annotation.ElapsedRealtimeLong;
import android.app.ActivityManager;
import android.content.ComponentName;
+import android.os.Flags;
import android.os.SystemClock;
import android.os.Trace;
import android.util.Slog;
@@ -378,12 +379,6 @@ final class ProcessStateRecord {
private boolean mReachable;
/**
- * Whether or not this process is reversed reachable from given process.
- */
- @GuardedBy("mService")
- private boolean mReversedReachable;
-
- /**
* The most recent time when the last visible activity within this process became invisible.
*
* <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is
@@ -996,16 +991,6 @@ final class ProcessStateRecord {
}
@GuardedBy("mService")
- boolean isReversedReachable() {
- return mReversedReachable;
- }
-
- @GuardedBy("mService")
- void setReversedReachable(boolean reversedReachable) {
- mReversedReachable = reversedReachable;
- }
-
- @GuardedBy("mService")
void resetCachedInfo() {
mCachedHasActivities = VALUE_INVALID;
mCachedIsHeavyWeight = VALUE_INVALID;
@@ -1366,7 +1351,12 @@ final class ProcessStateRecord {
}
if (mNotCachedSinceIdle) {
pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(mNotCachedSinceIdle);
- pw.print(" initialIdlePss="); pw.println(mApp.mProfile.getInitialIdlePss());
+ if (!Flags.removeAppProfilerPssCollection()) {
+ pw.print(" initialIdlePss=");
+ } else {
+ pw.print(" initialIdleRss=");
+ }
+ pw.println(mApp.mProfile.getInitialIdlePssOrRss());
}
if (hasTopUi() || hasOverlayUi() || mRunningRemoteAnimation) {
pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 599d99854b26..192fd6f1a9ce 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -123,6 +123,7 @@ public class SettingsToPropertiesMapper {
"angle",
"app_widgets",
"arc_next",
+ "avic",
"bluetooth",
"build",
"biometrics",
@@ -140,10 +141,12 @@ public class SettingsToPropertiesMapper {
"context_hub",
"core_experiments_team_internal",
"core_graphics",
+ "dck_framework",
"game",
"haptics",
"hardware_backed_security_mainline",
"input",
+ "lse_desktop_experience",
"machine_learning",
"mainline_modularization",
"mainline_sdk",
@@ -183,6 +186,7 @@ public class SettingsToPropertiesMapper {
"wear_security",
"wear_system_health",
"wear_systems",
+ "wear_sysui",
"window_surfaces",
"windowing_frontend",
};
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
index 108f53ff4485..0ee7d9cdd3d4 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
@@ -305,26 +305,6 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public boolean areUidModesDefault(int uid) {
- synchronized (mLock) {
- SparseIntArray opModes = mUidModes.get(uid);
- return (opModes == null || opModes.size() <= 0);
- }
- }
-
- @Override
- public boolean arePackageModesDefault(@NonNull String packageName, @UserIdInt int userId) {
- synchronized (mLock) {
- ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
- if (packageModes == null) {
- return true;
- }
- SparseIntArray opModes = packageModes.get(packageName);
- return (opModes == null || opModes.size() <= 0);
- }
- }
-
- @Override
public boolean removePackage(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
index 60d17cd388f6..f6e6bc0be8fa 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
@@ -124,21 +124,6 @@ public interface AppOpsCheckingServiceInterface {
void removeUid(int uid);
/**
- * Returns true if all uid modes for this uid are
- * in default state.
- * @param uid user id
- */
- boolean areUidModesDefault(int uid);
-
- /**
- * Returns true if all package modes for this package name are
- * in default state.
- * @param packageName package name.
- * @param userId user id associated with the package.
- */
- boolean arePackageModesDefault(String packageName, @UserIdInt int userId);
-
- /**
* Stop tracking app-op modes for all uid and packages.
*/
void clearAllModes();
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
index 3fee59bd1c2f..ccdf3a5baa7b 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
@@ -111,19 +111,6 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ
}
@Override
- public boolean areUidModesDefault(int uid) {
- Log.i(LOG_TAG, "areUidModesDefault(uid = " + uid + ")");
- return mService.areUidModesDefault(uid);
- }
-
- @Override
- public boolean arePackageModesDefault(String packageName, int userId) {
- Log.i(LOG_TAG, "arePackageModesDefault(packageName = " + packageName + ", userId = "
- + userId + ")");
- return mService.arePackageModesDefault(packageName, userId);
- }
-
- @Override
public void clearAllModes() {
Log.i(LOG_TAG, "clearAllModes()");
mService.clearAllModes();
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index c0cc8b176613..c3a02a84a277 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -168,28 +168,6 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ
}
@Override
- public boolean areUidModesDefault(int uid) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#areUidModesDefault");
- try {
- return mService.areUidModesDefault(uid);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean arePackageModesDefault(String packageName, @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#arePackageModesDefault");
- try {
- return mService.arePackageModesDefault(packageName, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
public void clearAllModes() {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#clearAllModes");
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 052b0c2078d1..14aab13bf638 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -148,6 +148,7 @@ import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.Clock;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -165,7 +166,6 @@ import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.policy.AppOpsPolicy;
import dalvik.annotation.optimization.NeverCompile;
@@ -531,19 +531,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- // Functions for uid mode access and manipulation.
- public SparseIntArray getNonDefaultUidModes() {
- return mAppOpsCheckingService.getNonDefaultUidModes(uid);
- }
-
- public int getUidMode(int op) {
- return mAppOpsCheckingService.getUidMode(uid, op);
- }
-
- public boolean setUidMode(int op, int mode) {
- return mAppOpsCheckingService.setUidMode(uid, op, mode);
- }
-
@SuppressWarnings("GuardedBy")
int evalMode(int op, int mode) {
return getUidStateTracker().evalMode(uid, op, mode);
@@ -613,15 +600,6 @@ public class AppOpsService extends IAppOpsService.Stub {
this.packageName = packageName;
}
- @Mode int getMode() {
- return mAppOpsCheckingService.getPackageMode(packageName, this.op,
- UserHandle.getUserId(this.uid));
- }
- void setMode(@Mode int mode) {
- mAppOpsCheckingService.setPackageMode(packageName, this.op, mode,
- UserHandle.getUserId(this.uid));
- }
-
void removeAttributionsWithNoTime() {
for (int i = mAttributions.size() - 1; i >= 0; i--) {
if (!mAttributions.valueAt(i).hasAnyTime()) {
@@ -653,7 +631,11 @@ public class AppOpsService extends IAppOpsService.Stub {
mAttributions.valueAt(i).createAttributedOpEntryLocked());
}
- return new OpEntry(op, getMode(), attributionEntries);
+ return new OpEntry(
+ op,
+ mAppOpsCheckingService.getPackageMode(
+ this.packageName, this.op, UserHandle.getUserId(this.uid)),
+ attributionEntries);
}
@NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
@@ -668,7 +650,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- return new OpEntry(op, getMode(), attributionEntries);
+ return new OpEntry(
+ op,
+ mAppOpsCheckingService.getPackageMode(
+ this.packageName, this.op, UserHandle.getUserId(this.uid)),
+ attributionEntries);
}
boolean isRunning() {
@@ -1384,8 +1370,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
final int code = foregroundOps.keyAt(fgi);
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
- && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)
+ && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true));
@@ -1405,7 +1393,11 @@ public class AppOpsService extends IAppOpsService.Stub {
if (op == null) {
continue;
}
- if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))
+ == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, listenerSet.valueAt(cbi), code, uidState.uid,
@@ -1497,7 +1489,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ final SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
if (opModes == null) {
return null;
}
@@ -1778,7 +1770,11 @@ public class AppOpsService extends IAppOpsService.Stub {
Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false);
if (ops != null) {
ops.remove(op.op);
- op.setMode(AppOpsManager.opToDefaultMode(op.op));
+ mAppOpsCheckingService.setPackageMode(
+ packageName,
+ op.op,
+ AppOpsManager.opToDefaultMode(op.op),
+ UserHandle.getUserId(op.uid));
if (ops.size() <= 0) {
UidState uidState = ops.uidState;
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
@@ -1848,15 +1844,16 @@ public class AppOpsService extends IAppOpsService.Stub {
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
}
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
- previousMode = uidState.getUidMode(code);
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)) {
+ previousMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
} else {
// doesn't look right but is legacy behavior.
previousMode = MODE_DEFAULT;
}
mIgnoredCallback = permissionPolicyCallback;
- if (!uidState.setUidMode(code, mode)) {
+ if (!mAppOpsCheckingService.setUidMode(uidState.uid, code, mode)) {
return;
}
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2133,10 +2130,15 @@ public class AppOpsService extends IAppOpsService.Stub {
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
if (op != null) {
- if (op.getMode() != mode) {
- previousMode = op.getMode();
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid))
+ != mode) {
+ previousMode =
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid));
mIgnoredCallback = permissionPolicyCallback;
- op.setMode(mode);
+ mAppOpsCheckingService.setPackageMode(op.packageName, op.op, mode,
+ UserHandle.getUserId(op.uid));
}
}
}
@@ -2274,7 +2276,7 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
@@ -2283,7 +2285,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int previousMode = opModes.valueAt(j);
int newMode = isUidOpGrantedByRole(uidState.uid, code) ? MODE_ALLOWED :
AppOpsManager.opToDefaultMode(code);
- uidState.setUidMode(code, newMode);
+ mAppOpsCheckingService.setUidMode(uidState.uid, code, newMode);
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -2325,14 +2327,22 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
if (AppOpsManager.opAllowsReset(curOp.op)) {
- int previousMode = curOp.getMode();
+ int previousMode =
+ mAppOpsCheckingService.getPackageMode(
+ curOp.packageName,
+ curOp.op,
+ UserHandle.getUserId(curOp.uid));
int newMode = isPackageOpGrantedByRole(packageName, uidState.uid,
curOp.op) ? MODE_ALLOWED : AppOpsManager.opToDefaultMode(
curOp.op);
if (previousMode == newMode) {
continue;
}
- curOp.setMode(newMode);
+ mAppOpsCheckingService.setPackageMode(
+ curOp.packageName,
+ curOp.op,
+ newMode,
+ UserHandle.getUserId(curOp.uid));
changed = true;
uidChanged = true;
final int uid = curOp.uidState.uid;
@@ -2391,7 +2401,8 @@ public class AppOpsService extends IAppOpsService.Stub {
long token = Binder.clearCallingIdentity();
try {
// Permissions are managed by UIDs, but unfortunately a package name is required in API.
- String packageName = ArrayUtils.firstOrNull(packageManager.getPackagesForUid(uid));
+ String packageName = ArrayUtils.firstOrNull(ArrayUtils.defeatNullable(
+ packageManager.getPackagesForUid(uid)));
if (packageName == null) {
return false;
}
@@ -2591,15 +2602,22 @@ public class AppOpsService extends IAppOpsService.Stub {
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null
- && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
- final int rawMode = uidState.getUidMode(code);
+ && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)) {
+ final int rawMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return raw ? op.getMode() : op.uidState.evalMode(op.op, op.getMode());
+ return raw
+ ? mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid))
+ : op.uidState.evalMode(
+ op.op,
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid)));
}
}
@@ -2835,8 +2853,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -2849,7 +2870,13 @@ public class AppOpsService extends IAppOpsService.Stub {
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -3371,8 +3398,11 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3387,7 +3417,13 @@ public class AppOpsService extends IAppOpsService.Stub {
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
@@ -3477,8 +3513,11 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default mode per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3490,7 +3529,13 @@ public class AppOpsService extends IAppOpsService.Stub {
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) {
@@ -5619,7 +5664,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
- final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ final SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
@@ -5647,7 +5693,12 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (!hasMode) {
for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
- if (ops.valueAt(opi).getMode() == dumpMode) {
+ final Op op = ops.valueAt(opi);
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))
+ == dumpMode) {
hasMode = true;
}
}
@@ -5698,7 +5749,12 @@ public class AppOpsService extends IAppOpsService.Stub {
if (dumpOp >= 0 && dumpOp != opCode) {
continue;
}
- if (dumpMode >= 0 && dumpMode != op.getMode()) {
+ if (dumpMode >= 0
+ && dumpMode
+ != mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))) {
continue;
}
if (!printedPackage) {
@@ -5706,14 +5762,25 @@ public class AppOpsService extends IAppOpsService.Stub {
printedPackage = true;
}
pw.print(" "); pw.print(AppOpsManager.opToName(opCode));
- pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode()));
+ pw.print(" (");
+ pw.print(
+ AppOpsManager.modeToName(
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))));
final int switchOp = AppOpsManager.opToSwitch(opCode);
if (switchOp != opCode) {
pw.print(" / switch ");
pw.print(AppOpsManager.opToName(switchOp));
final Op switchObj = ops.get(switchOp);
- int mode = switchObj == null
- ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
+ int mode =
+ switchObj == null
+ ? AppOpsManager.opToDefaultMode(switchOp)
+ : mAppOpsCheckingService.getPackageMode(
+ switchObj.packageName,
+ switchObj.op,
+ UserHandle.getUserId(switchObj.uid));
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
@@ -5847,7 +5914,13 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
Ops ops = uidState.pkgOps.valueAt(pkgNum);
Op op = ops != null ? ops.get(code) : null;
- if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
+ if (op == null) {
+ continue;
+ }
+ final int mode =
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid));
+ if (mode != MODE_ALLOWED && mode != MODE_FOREGROUND) {
continue;
}
int numAttrTags = op.mAttributions.size();
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
index de73a5514792..98e6476e9707 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
@@ -149,30 +149,6 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
- public boolean areUidModesDefault(int uid) {
- boolean oldVal = mOldImplementation.areUidModesDefault(uid);
- boolean newVal = mNewImplementation.areUidModesDefault(uid);
-
- if (oldVal != newVal) {
- signalImplDifference("areUidModesDefault");
- }
-
- return newVal;
- }
-
- @Override
- public boolean arePackageModesDefault(String packageName, int userId) {
- boolean oldVal = mOldImplementation.arePackageModesDefault(packageName, userId);
- boolean newVal = mNewImplementation.arePackageModesDefault(packageName, userId);
-
- if (oldVal != newVal) {
- signalImplDifference("arePackageModesDefault");
- }
-
- return newVal;
- }
-
- @Override
public void clearAllModes() {
mOldImplementation.clearAllModes();
mNewImplementation.clearAllModes();
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 51cb9505ed4f..5c8dd0d427f9 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.media.Utils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -167,8 +168,9 @@ import java.util.Objects;
public String toString() {
return "type: " + mDeviceType
+ " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
- + " addr: " + mDeviceAddress + " bt audio type: "
- + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
+ + " addr: " + Utils.anonymizeBluetoothAddress(mInternalDeviceType, mDeviceAddress)
+ + " bt audio type: "
+ + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
+ " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker
+ " HTenabled: " + mHeadTrackerEnabled;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 5d4f711b9432..b1706ed61e36 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2747,5 +2747,4 @@ public class AudioDeviceBroker {
void clearDeviceInventory() {
mDeviceInventory.clearDeviceInventory();
}
-
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 7ba0827f2016..e9b102bc67b8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -48,6 +48,7 @@ import android.media.IStrategyNonDefaultDevicesDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.media.MediaRecorder.AudioSource;
+import android.media.Utils;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
@@ -477,7 +478,7 @@ public class AudioDeviceInventory {
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
+ " (" + AudioSystem.getDeviceName(mDeviceType)
+ ") name:" + mDeviceName
- + " addr:" + mDeviceAddress
+ + " addr:" + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceAddress)
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
+ " peer addr:" + mPeerDeviceAddress
+ " group:" + mGroupId
@@ -532,7 +533,7 @@ public class AudioDeviceInventory {
mApmConnectedDevices.forEach((keyType, valueAddress) -> {
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType)
- + ") addr:" + valueAddress); });
+ + ") addr:" + Utils.anonymizeBluetoothAddress(keyType, valueAddress)); });
pw.println("\n" + prefix + "Preferred devices for capture preset:");
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
pw.println(" " + prefix + "capturePreset:" + capturePreset
@@ -1789,7 +1790,8 @@ public class AudioDeviceInventory {
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "A2DP device addr=" + address + " now available").printLog(TAG));
+ "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printLog(TAG));
}
// Reset A2DP suspend state each time a new sink is connected
@@ -2027,7 +2029,8 @@ public class AudioDeviceInventory {
.equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
+ "A2DP device " + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable, was not used")).printLog(TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
@@ -2043,13 +2046,15 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM failed to make unavailable A2DP device addr=" + address
+ "APM failed to make unavailable A2DP device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ " error=" + res).printLog(TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "A2DP device addr=" + address + " made unavailable")).printLog(TAG));
+ "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2238,7 +2243,8 @@ public class AudioDeviceInventory {
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "LE Audio device addr=" + address + " now available").printLog(TAG));
+ "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printLog(TAG));
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
@@ -2282,7 +2288,8 @@ public class AudioDeviceInventory {
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "LE Audio device addr=" + address + " made unavailable").printLog(TAG));
+ "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable").printLog(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 99321c44931b..1ef4333ddbd8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,8 +16,8 @@
package com.android.server.audio;
-import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
+import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -29,7 +29,6 @@ import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_A2DP;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioManager.STREAM_SYSTEM;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
@@ -119,6 +118,7 @@ import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IDevicesForAttributesCallback;
+import android.media.ILoudnessCodecUpdatesDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IPreferredMixerAttributesDispatcher;
@@ -133,10 +133,12 @@ import android.media.IStrategyNonDefaultDevicesDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IStreamAliasingDispatcher;
import android.media.IVolumeController;
+import android.media.LoudnessCodecFormat;
import android.media.MediaMetrics;
import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
import android.media.Spatializer;
+import android.media.Utils;
import android.media.VolumeInfo;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
@@ -409,7 +411,6 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_RESET_SPATIALIZER = 50;
private static final int MSG_NO_LOG_FOR_PLAYER_I = 51;
private static final int MSG_DISPATCH_PREFERRED_MIXER_ATTRIBUTES = 52;
- private static final int MSG_LOWER_VOLUME_TO_RS1 = 53;
private static final int MSG_CONFIGURATION_CHANGED = 54;
private static final int MSG_BROADCAST_MASTER_MUTE = 55;
@@ -2900,7 +2901,7 @@ public class AudioService extends IAudioService.Stub
// IPC methods
///////////////////////////////////////////////////////////////////////////
/**
- * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
* @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
* List<AudioDeviceAttributes>)
*/
@@ -3536,7 +3537,7 @@ public class AudioService extends IAudioService.Stub
return;
}
- mSoundDoseHelper.invalidatPendingVolumeCommand();
+ mSoundDoseHelper.invalidatePendingVolumeCommand();
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
@@ -4268,7 +4269,8 @@ public class AudioService extends IAudioService.Stub
if (device == null) {
// call was already logged in setDeviceVolume()
sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
- index/*val1*/, flags/*val2*/, callingPackage));
+ index/*val1*/, flags/*val2*/, getStreamVolume(streamType) /*val3*/,
+ callingPackage));
}
setStreamVolume(streamType, index, flags, device,
callingPackage, callingPackage, attributionTag,
@@ -4371,6 +4373,7 @@ public class AudioService extends IAudioService.Stub
if (mMediaPlaybackActive.getAndSet(mediaActive) != mediaActive && mediaActive) {
mSoundDoseHelper.scheduleMusicActiveCheck();
}
+
// Update playback active state for all apps in audio mode stack.
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
// and request an audio mode update immediately. Upon any other change, queue the message
@@ -4607,7 +4610,7 @@ public class AudioService extends IAudioService.Stub
return;
}
- mSoundDoseHelper.invalidatPendingVolumeCommand();
+ mSoundDoseHelper.invalidatePendingVolumeCommand();
oldIndex = streamState.getIndex(device);
@@ -7472,7 +7475,7 @@ public class AudioService extends IAudioService.Stub
sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
- + device.getAddress() + " behavior:"
+ + Utils.anonymizeBluetoothAddress(device.getAddress()) + " behavior:"
+ AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior)
+ " pack:" + pkgName).printLog(TAG));
if (pkgName == null) {
@@ -8782,7 +8785,7 @@ public class AudioService extends IAudioService.Stub
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
mStreamVolumeAlias[mStreamType]);
AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
- mStreamType, mStreamVolumeAlias[mStreamType], index));
+ mStreamType, mStreamVolumeAlias[mStreamType], index, oldIndex));
sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
}
}
@@ -9544,10 +9547,6 @@ public class AudioService extends IAudioService.Stub
onDispatchPreferredMixerAttributesChanged(msg.getData(), msg.arg1);
break;
- case MSG_LOWER_VOLUME_TO_RS1:
- onLowerVolumeToRs1();
- break;
-
case MSG_CONFIGURATION_CHANGED:
onConfigurationChanged();
break;
@@ -9562,6 +9561,7 @@ public class AudioService extends IAudioService.Stub
case SoundDoseHelper.MSG_PERSIST_MUSIC_ACTIVE_MS:
case SoundDoseHelper.MSG_PERSIST_CSD_VALUES:
case SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION:
+ case SoundDoseHelper.MSG_LOWER_VOLUME_TO_RS1:
mSoundDoseHelper.handleMessage(msg);
break;
@@ -9646,7 +9646,7 @@ public class AudioService extends IAudioService.Stub
private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.enqueue(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
- + address + " support=" + support).printLog(TAG));
+ + Utils.anonymizeBluetoothAddress(address) + " support=" + support).printLog(TAG));
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
setAvrcpAbsoluteVolumeSupported(support);
}
@@ -10544,11 +10544,11 @@ public class AudioService extends IAudioService.Stub
AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
Objects.requireNonNull(ada);
if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
- String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
+ String anonymizedAddress = Utils.anonymizeBluetoothAddress(ada.getAddress());
for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
&& (ada.getInternalType() == ads.getInternalDeviceType())
- && anonymizedAddress.equals(anonymizeBluetoothAddress(
+ && anonymizedAddress.equals(Utils.anonymizeBluetoothAddress(
ads.getDeviceAddress())))) {
continue;
}
@@ -10559,19 +10559,6 @@ public class AudioService extends IAudioService.Stub
return ada;
}
- /**
- * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
- * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
- * @param address Mac address to be anonymized
- * @return anonymized mac address
- */
- static String anonymizeBluetoothAddress(String address) {
- if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
- return null;
- }
- return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
- }
-
private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
List<AudioDeviceAttributes> devices) {
if (isBluetoothPrividged()) {
@@ -10595,7 +10582,7 @@ public class AudioService extends IAudioService.Stub
return ada;
}
AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
- res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
+ res.setAddress(Utils.anonymizeBluetoothAddress(ada.getAddress()));
return res;
}
@@ -10607,6 +10594,51 @@ public class AudioService extends IAudioService.Stub
return anonymizeAudioDeviceAttributesUnchecked(ada);
}
+ // ========================================================================================
+ // LoudnessCodecConfigurator
+
+ @Override
+ public void registerLoudnessCodecUpdatesDispatcher(ILoudnessCodecUpdatesDispatcher dispatcher) {
+ // TODO: implement
+ }
+
+ @Override
+ public void unregisterLoudnessCodecUpdatesDispatcher(
+ ILoudnessCodecUpdatesDispatcher dispatcher) {
+ // TODO: implement
+ }
+
+ @Override
+ public void startLoudnessCodecUpdates(int piid) {
+ // TODO: implement
+ }
+
+ @Override
+ public void stopLoudnessCodecUpdates(int piid) {
+ // TODO: implement
+ }
+
+ @Override
+ public void addLoudnesssCodecFormat(int piid, LoudnessCodecFormat format) {
+ // TODO: implement
+ }
+
+ @Override
+ public void addLoudnesssCodecFormatList(int piid, List<LoudnessCodecFormat> format) {
+ // TODO: implement
+ }
+
+ @Override
+ public void removeLoudnessCodecFormat(int piid, LoudnessCodecFormat format) {
+ // TODO: implement
+ }
+
+ @Override
+ public PersistableBundle getLoudnessParams(int piid, LoudnessCodecFormat format) {
+ // TODO: implement
+ return null;
+ }
+
//==========================================================================================
// camera sound is forced if any of the resources corresponding to one active SIM
@@ -10946,31 +10978,11 @@ public class AudioService extends IAudioService.Stub
}
/*package*/ void postLowerVolumeToRs1() {
- sendMsg(mAudioHandler, MSG_LOWER_VOLUME_TO_RS1, SENDMSG_QUEUE,
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_LOWER_VOLUME_TO_RS1, SENDMSG_QUEUE,
// no params, no delay
0, 0, null, 0);
}
- /**
- * Called when handling MSG_LOWER_VOLUME_TO_RS1
- */
- private void onLowerVolumeToRs1() {
- final ArrayList<AudioDeviceAttributes> devices = getDevicesForAttributesInt(
- new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(), true);
- final int nativeDeviceType;
- final AudioDeviceAttributes ada;
- if (!devices.isEmpty()) {
- ada = devices.get(0);
- nativeDeviceType = ada.getInternalType();
- } else {
- nativeDeviceType = AudioSystem.DEVICE_OUT_USB_HEADSET;
- ada = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_USB_HEADSET, "");
- }
- final int index = mSoundDoseHelper.safeMediaVolumeIndex(nativeDeviceType);
- setStreamVolumeWithAttributionInt(STREAM_MUSIC, index, /*flags*/ 0, ada,
- "com.android.server.audio", "AudioService");
- }
-
@Override
@android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public float getOutputRs2UpperBound() {
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index aac868f45fe3..f69b9f6523cc 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -151,11 +151,13 @@ public class AudioServiceEvents {
final int mStreamType;
final int mAliasStreamType;
final int mIndex;
+ final int mOldIndex;
- VolChangedBroadcastEvent(int stream, int alias, int index) {
+ VolChangedBroadcastEvent(int stream, int alias, int index, int oldIndex) {
mStreamType = stream;
mAliasStreamType = alias;
mIndex = index;
+ mOldIndex = oldIndex;
}
@Override
@@ -163,7 +165,8 @@ public class AudioServiceEvents {
return new StringBuilder("sending VOLUME_CHANGED stream:")
.append(AudioSystem.streamToString(mStreamType))
.append(" index:").append(mIndex)
- .append(" alias:").append(AudioSystem.streamToString(mAliasStreamType))
+ .append(" (was:").append(mOldIndex)
+ .append(") alias:").append(AudioSystem.streamToString(mAliasStreamType))
.toString();
}
}
@@ -234,19 +237,35 @@ public class AudioServiceEvents {
final int mStream;
final int mVal1;
final int mVal2;
+ final int mVal3;
final String mCaller;
final String mGroupName;
+ /** used for VOL_SET_STREAM_VOL */
+ VolumeEvent(int op, int stream, int val1, int val2, int val3, String caller) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = val1;
+ mVal2 = val2;
+ mVal3 = val3;
+ mCaller = caller;
+ // unused
+ mGroupName = null;
+ logMetricEvent();
+ }
+
/** used for VOL_ADJUST_VOL_UID,
* VOL_ADJUST_SUGG_VOL,
* VOL_ADJUST_STREAM_VOL,
- * VOL_SET_STREAM_VOL */
+ */
VolumeEvent(int op, int stream, int val1, int val2, String caller) {
mOp = op;
mStream = stream;
mVal1 = val1;
mVal2 = val2;
mCaller = caller;
+ // unused
+ mVal3 = -1;
mGroupName = null;
logMetricEvent();
}
@@ -257,6 +276,7 @@ public class AudioServiceEvents {
mVal1 = index;
mVal2 = gainDb;
// unused
+ mVal3 = -1;
mStream = -1;
mCaller = null;
mGroupName = null;
@@ -269,6 +289,7 @@ public class AudioServiceEvents {
mVal1 = index;
// unused
mVal2 = 0;
+ mVal3 = -1;
mStream = -1;
mCaller = null;
mGroupName = null;
@@ -282,6 +303,7 @@ public class AudioServiceEvents {
mVal1 = index;
mVal2 = voiceActive ? 1 : 0;
// unused
+ mVal3 = -1;
mCaller = null;
mGroupName = null;
logMetricEvent();
@@ -294,6 +316,7 @@ public class AudioServiceEvents {
mVal1 = index;
mVal2 = mode;
// unused
+ mVal3 = -1;
mCaller = null;
mGroupName = null;
logMetricEvent();
@@ -308,6 +331,8 @@ public class AudioServiceEvents {
mVal2 = flags;
mCaller = caller;
mGroupName = group;
+ // unused
+ mVal3 = -1;
logMetricEvent();
}
@@ -317,8 +342,10 @@ public class AudioServiceEvents {
mStream = stream;
mVal1 = state ? 1 : 0;
mVal2 = 0;
+ // unused
mCaller = null;
mGroupName = null;
+ mVal3 = -1;
logMetricEvent();
}
@@ -328,6 +355,8 @@ public class AudioServiceEvents {
mStream = -1;
mVal1 = state ? 1 : 0;
mVal2 = 0;
+ // unused
+ mVal3 = -1;
mCaller = null;
mGroupName = null;
logMetricEvent();
@@ -386,6 +415,7 @@ public class AudioServiceEvents {
.set(MediaMetrics.Property.EVENT, "setStreamVolume")
.set(MediaMetrics.Property.FLAGS, mVal2)
.set(MediaMetrics.Property.INDEX, mVal1)
+ .set(MediaMetrics.Property.OLD_INDEX, mVal3)
.set(MediaMetrics.Property.STREAM_TYPE,
AudioSystem.streamToString(mStream))
.record();
@@ -478,6 +508,7 @@ public class AudioServiceEvents {
.append(AudioSystem.streamToString(mStream))
.append(" index:").append(mVal1)
.append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(" oldIndex:").append(mVal3)
.append(") from ").append(mCaller)
.toString();
case VOL_SET_HEARING_AID_VOL:
@@ -539,6 +570,8 @@ public class AudioServiceEvents {
static final int DOSE_UPDATE = 1;
static final int DOSE_REPEAT_5X = 2;
static final int DOSE_ACCUMULATION_START = 3;
+ static final int LOWER_VOLUME_TO_RS1 = 4;
+
final int mEventType;
final float mFloatValue;
final long mLongValue;
@@ -565,6 +598,10 @@ public class AudioServiceEvents {
return new SoundDoseEvent(DOSE_ACCUMULATION_START, 0 /*ignored*/, 0 /*ignored*/);
}
+ static SoundDoseEvent getLowerVolumeToRs1Event() {
+ return new SoundDoseEvent(LOWER_VOLUME_TO_RS1, 0 /*ignored*/, 0 /*ignored*/);
+ }
+
@Override
public String eventToString() {
switch (mEventType) {
@@ -578,6 +615,8 @@ public class AudioServiceEvents {
return "CSD reached 500%";
case DOSE_ACCUMULATION_START:
return "CSD accumulating: RS2 entered";
+ case LOWER_VOLUME_TO_RS1:
+ return "CSD lowering volume to RS1";
}
return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
}
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index d65c7c2c526d..793752f3a1be 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -18,6 +18,7 @@ package com.android.server.audio;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
+import static android.media.AudioManager.STREAM_MUSIC;
import static com.android.server.audio.AudioService.MAX_STREAM_VOLUME;
import static com.android.server.audio.AudioService.MIN_STREAM_VOLUME;
@@ -31,6 +32,8 @@ import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.ISoundDose;
@@ -115,6 +118,9 @@ public class SoundDoseHelper {
/*package*/ static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
/*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 6;
+ /*package*/ static final int MSG_LOWER_VOLUME_TO_RS1 = SAFE_MEDIA_VOLUME_MSG_START + 7;
+
+
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
private static final int MOMENTARY_EXPOSURE_TIMEOUT_MS = (20 * 3600 * 1000); // 20 hours
@@ -774,7 +780,7 @@ public class SoundDoseHelper {
return mSafeMediaVolumeDevices.get(device, SAFE_MEDIA_VOLUME_UNINITIALIZED) >= 0;
}
- /*package*/ void invalidatPendingVolumeCommand() {
+ /*package*/ void invalidatePendingVolumeCommand() {
synchronized (mSafeMediaVolumeStateLock) {
mPendingVolumeCommand = null;
}
@@ -808,6 +814,9 @@ public class SoundDoseHelper {
updateDoseAttenuation(streamState.getIndex(device), device,
streamState.getStreamType(), isAbsoluteVolume);
break;
+ case MSG_LOWER_VOLUME_TO_RS1:
+ onLowerVolumeToRs1();
+ break;
default:
Log.e(TAG, "Unexpected msg to handle: " + msg.what);
break;
@@ -1272,6 +1281,25 @@ public class SoundDoseHelper {
return value;
}
+ /** Called when handling MSG_LOWER_VOLUME_TO_RS1 */
+ private void onLowerVolumeToRs1() {
+ mLogger.enqueue(SoundDoseEvent.getLowerVolumeToRs1Event());
+ final ArrayList<AudioDeviceAttributes> devices = mAudioService.getDevicesForAttributesInt(
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(), true);
+ final int nativeDeviceType;
+ final AudioDeviceAttributes ada;
+ if (!devices.isEmpty()) {
+ ada = devices.get(0);
+ nativeDeviceType = ada.getInternalType();
+ } else {
+ nativeDeviceType = AudioSystem.DEVICE_OUT_USB_HEADSET;
+ ada = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_USB_HEADSET, "");
+ }
+ final int index = safeMediaVolumeIndex(nativeDeviceType);
+ mAudioService.setStreamVolumeWithAttributionInt(STREAM_MUSIC, index / 10, /*flags*/ 0, ada,
+ mContext.getOpPackageName(), /*attributionTag=*/null);
+ }
+
// StreamVolumeCommand contains the information needed to defer the process of
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
private static class StreamVolumeCommand {
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index b9ccbfbf55e7..c5073001a672 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -576,7 +576,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onDialogAnimatedIn(boolean startFingerprintNow) {
- if (mState != STATE_AUTH_STARTED) {
+ if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI) {
Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
return;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index 858bb864d4db..e70e25aebe9b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -75,4 +75,11 @@ public class AidlSession {
public boolean hasContextMethods() {
return mHalInterfaceVersion >= 2;
}
+
+ /**
+ * If this backend implements enroll methods with an {@link android.view.Surface}.
+ */
+ public boolean supportsFaceEnrollOptions() {
+ return mHalInterfaceVersion >= 4;
+ }
}
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 dbed1f7a8f9d..0af6e40434ef 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
@@ -22,6 +22,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.FaceEnrollOptions;
import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.common.NativeHandle;
@@ -201,9 +202,21 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
if (session.hasContextMethods()) {
final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().enrollWithContext(
- hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle,
- opContext.toAidlContext());
+ ICancellationSignal cancel;
+ if (session.supportsFaceEnrollOptions()) {
+ FaceEnrollOptions options = new FaceEnrollOptions();
+ options.hardwareAuthToken = hat;
+ options.enrollmentType = EnrollmentType.DEFAULT;
+ options.features = features;
+ options.nativeHandlePreview = null;
+ options.context = opContext.toAidlContext();
+ options.surfacePreview = mPreviewSurface;
+ cancel = session.getSession().enrollWithOptions(options);
+ } else {
+ cancel = session.getSession().enrollWithContext(
+ hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle,
+ opContext.toAidlContext());
+ }
getBiometricContext().subscribe(opContext, ctx -> {
try {
session.getSession().onContextChanged(ctx);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 4fc2e22cfae3..092371193e98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -20,6 +20,7 @@ import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.EnrollmentStageConfig;
import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.face.FaceEnrollOptions;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
@@ -212,6 +213,12 @@ public class TestHal extends IFace.Stub {
public void onContextChanged(OperationContext context) {
Slog.w(TAG, "onContextChanged");
}
+
+ @Override
+ public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
+ return enroll(options.hardwareAuthToken, options.enrollmentType, options.features,
+ options.nativeHandlePreview);
+ }
};
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
index eecf44b92918..489b213677dd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.EnrollmentStageConfig;
+import android.hardware.biometrics.face.FaceEnrollOptions;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.OptionalBool;
@@ -344,4 +345,10 @@ public class AidlToHidlAdapter implements ISession {
return null;
}
}
+
+ @Override
+ public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
+ //Unsupported in HIDL
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 283353ddc25d..c629b2b91603 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -25,6 +25,7 @@ import android.os.LocaleList;
import android.util.ArraySet;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Virtual device manager local service interface.
@@ -32,29 +33,12 @@ import java.util.Set;
*/
public abstract class VirtualDeviceManagerInternal {
- /** Interface to listen to the creation and destruction of virtual displays. */
- public interface VirtualDisplayListener {
- /** Notifies that a virtual display was created. */
- void onVirtualDisplayCreated(int displayId);
-
- /** Notifies that a virtual display was removed. */
- void onVirtualDisplayRemoved(int displayId);
- }
-
/** Interface to listen to the changes on the list of app UIDs running on any virtual device. */
public interface AppsOnVirtualDeviceListener {
/** Notifies that running apps on any virtual device has changed */
void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids);
}
- /** Register a listener for the creation and destruction of virtual displays. */
- public abstract void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
- /** Unregister a listener for the creation and destruction of virtual displays. */
- public abstract void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
/** Register a listener for changes of running app UIDs on any virtual device. */
public abstract void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
@@ -63,6 +47,14 @@ public abstract class VirtualDeviceManagerInternal {
public abstract void unregisterAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
+ /** Register a listener for removal of persistent device IDs. */
+ public abstract void registerPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener);
+
+ /** Unregister a listener for the removal of persistent device IDs. */
+ public abstract void unregisterPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener);
+
/**
* Notifies that the set of apps running on virtual devices has changed.
* This method only notifies the listeners when the union of running UIDs on all virtual devices
@@ -76,6 +68,11 @@ public abstract class VirtualDeviceManagerInternal {
public abstract void onAuthenticationPrompt(int uid);
/**
+ * Notifies the given persistent device IDs have been removed.
+ */
+ public abstract void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds);
+
+ /**
* Gets the owner uid for a deviceId.
*
* @param deviceId which device we're asking about
@@ -104,13 +101,6 @@ public abstract class VirtualDeviceManagerInternal {
public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid);
/**
- * Notifies that a virtual display is created.
- *
- * @param displayId The display id of the created virtual display.
- */
- public abstract void onVirtualDisplayCreated(int displayId);
-
- /**
* Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 53fbe8f37046..aef224843b2f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
@@ -45,12 +44,10 @@ import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -113,7 +110,6 @@ import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -152,7 +148,6 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.NetdUtils;
@@ -202,7 +197,6 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -1063,8 +1057,6 @@ public class Vpn {
// Store mPackage since it might be reset or might be replaced with the other VPN app.
final String oldPackage = mPackage;
final boolean isPackageChanged = !Objects.equals(packageName, oldPackage);
- // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from
- // ConnectivityServiceTest.
// Only notify VPN apps that were already always-on, and only if the always-on provider
// changed, or the lockdown mode changed.
final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn
@@ -1078,12 +1070,6 @@ public class Vpn {
saveAlwaysOnPackage();
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (!SdkLevel.isAtLeastT()) {
- return true;
- }
-
if (shouldNotifyOldPkg) {
// If both of shouldNotifyOldPkg & isPackageChanged are true, that means the
// always-on of old package is disabled or the old package is replaced with the new
@@ -1984,9 +1970,7 @@ public class Vpn {
for (String app : packageNames) {
int uid = getAppUid(mContext, app, userId);
if (uid != -1) uids.add(uid);
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (Process.isApplicationUid(uid) && SdkLevel.isAtLeastT()) {
+ if (Process.isApplicationUid(uid)) {
uids.add(Process.toSdkSandboxUid(uid));
}
}
@@ -2297,15 +2281,6 @@ public class Vpn {
private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
@Override
- public void interfaceStatusChanged(String interfaze, boolean up) {
- synchronized (Vpn.this) {
- if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) {
- ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze);
- }
- }
- }
-
- @Override
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
@@ -2556,17 +2531,6 @@ public class Vpn {
private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
- private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
- for (RouteInfo route : prop.getAllRoutes()) {
- // Currently legacy VPN only works on IPv4.
- if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
- return route;
- }
- }
-
- throw new IllegalStateException("Unable to find IPv4 default gateway");
- }
-
private void enforceNotRestrictedUser() {
final long token = Binder.clearCallingIdentity();
try {
@@ -2585,15 +2549,14 @@ public class Vpn {
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, Network, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, @Nullable Network underlying,
- LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, underlying, egress);
+ startLegacyVpnPrivileged(profile);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2652,23 +2615,19 @@ public class Vpn {
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, Network, LinkProperties)}, but does not
- * check permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile)}, but does not check permissions under
+ * the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
- public void startLegacyVpnPrivileged(VpnProfile profile,
- @Nullable Network underlying, @NonNull LinkProperties egress) {
+ public void startLegacyVpnPrivileged(VpnProfile profileToStart) {
+ final VpnProfile profile = profileToStart.clone();
UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
- final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
- final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
- final String iface = ipv4DefaultRoute.getInterface();
-
// Load certificates.
String privateKey = "";
String userCert = "";
@@ -2700,8 +2659,6 @@ public class Vpn {
throw new IllegalStateException("Cannot load credentials");
}
- // Prepare arguments for racoon.
- String[] racoon = null;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
// Secret key is still just the alias (not the actual private key). The private key
@@ -2731,109 +2688,9 @@ public class Vpn {
// profile.
startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- racoon = new String[] {
- iface, profile.server, "udppsk", profile.ipsecIdentifier,
- profile.ipsecSecret, "1701",
- };
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- racoon = new String[] {
- iface, profile.server, "udprsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, "1701",
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
- racoon = new String[] {
- iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
- profile.ipsecSecret, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
- racoon = new String[] {
- iface, profile.server, "xauthrsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
- racoon = new String[] {
- iface, profile.server, "hybridrsa",
- caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
}
- // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
- // because LegacyVpn.
- // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
- // - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
- // - 28 (464xlat)
- String[] mtpd = null;
- switch (profile.type) {
- case VpnProfile.TYPE_PPTP:
- mtpd = new String[] {
- iface, "pptp", profile.server, "1723",
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- (profile.mppe ? "+mppe" : "nomppe"),
- };
- if (profile.mppe) {
- // Disallow PAP authentication when MPPE is requested, as MPPE cannot work
- // with PAP anyway, and users may not expect PAP (plain text) to be used when
- // MPPE was requested.
- mtpd = Arrays.copyOf(mtpd, mtpd.length + 1);
- mtpd[mtpd.length - 1] = "-pap";
- }
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- mtpd = new String[] {
- iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- };
- break;
- }
-
- VpnConfig config = new VpnConfig();
- config.legacy = true;
- config.user = profile.key;
- config.interfaze = iface;
- config.session = profile.name;
- config.isMetered = false;
- config.proxyInfo = profile.proxy;
- if (underlying != null) {
- config.underlyingNetworks = new Network[] { underlying };
- }
-
- config.addLegacyRoutes(profile.routes);
- if (!profile.dnsServers.isEmpty()) {
- config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
- }
- if (!profile.searchDomains.isEmpty()) {
- config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
- }
- startLegacyVpn(config, racoon, mtpd, profile);
- }
-
- private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd,
- VpnProfile profile) {
- stopVpnRunnerPrivileged();
-
- // Prepare for the new request.
- prepareInternal(VpnConfig.LEGACY_VPN);
- updateState(DetailedState.CONNECTING, "startLegacyVpn");
-
- // Start a new LegacyVpnRunner and we are done!
- mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile);
- startLegacyVpnRunner();
- }
-
- @VisibleForTesting
- protected void startLegacyVpnRunner() {
- mVpnRunner.start();
+ throw new UnsupportedOperationException("Legacy VPN is deprecated");
}
/**
@@ -2851,17 +2708,7 @@ public class Vpn {
return;
}
- final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner;
mVpnRunner.exit();
-
- // LegacyVpn uses daemons that must be shut down before new ones are brought up.
- // The same limitation does not apply to Platform VPNs.
- if (isLegacyVpn) {
- synchronized (LegacyVpnRunner.TAG) {
- // wait for old thread to completely finish before spinning up
- // new instance, otherwise state updates can be out of order.
- }
- }
}
/**
@@ -3537,6 +3384,13 @@ public class Vpn {
* given network to start a new IKE session.
*/
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
+ synchronized (Vpn.this) {
+ // Ignore stale runner.
+ if (mVpnRunner != this) return;
+ setVpnNetworkPreference(mSessionKey,
+ createUserAndRestrictedProfilesRanges(mUserId,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
+ }
if (underlyingNetwork == null) {
// For null underlyingNetwork case, there will not be a NetworkAgent available so
// no underlying network update is necessary here. Note that updating
@@ -4057,6 +3911,7 @@ public class Vpn {
updateState(DetailedState.FAILED, exception.getMessage());
}
+ clearVpnNetworkPreference(mSessionKey);
disconnectVpnRunner();
}
@@ -4143,9 +3998,7 @@ public class Vpn {
// Ignore stale runner.
if (mVpnRunner != this) return;
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT() && category != null && isVpnApp(mPackage)) {
+ if (category != null && isVpnApp(mPackage)) {
sendEventToVpnManagerApp(category, errorClass, errorCode,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
@@ -4193,6 +4046,13 @@ public class Vpn {
}
resetIkeState();
+ if (errorCode != VpnManager.ERROR_CODE_NETWORK_LOST
+ // Clear the VPN network preference when the retry delay is higher than 5s.
+ // mRetryCount was increased when scheduleRetryNewIkeSession() is called,
+ // therefore use mRetryCount - 1 here.
+ && mDeps.getNextRetryDelayMs(mRetryCount - 1) > 5_000L) {
+ clearVpnNetworkPreference(mSessionKey);
+ }
}
/**
@@ -4239,13 +4099,17 @@ public class Vpn {
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
}
@Override
public void exitVpnRunner() {
+ // mSessionKey won't be changed since the Ikev2VpnRunner is created, so it's ok to use
+ // it outside the mExecutor. And clearing the VPN network preference here can prevent
+ // the case that the VPN network preference isn't cleared when Ikev2VpnRunner became
+ // stale.
+ clearVpnNetworkPreference(mSessionKey);
try {
mExecutor.execute(() -> {
disconnectVpnRunner();
@@ -4256,343 +4120,6 @@ public class Vpn {
}
}
- /**
- * Bringing up a VPN connection takes time, and that is all this thread
- * does. Here we have plenty of time. The only thing we need to take
- * care of is responding to interruptions as soon as possible. Otherwise
- * requests will pile up. This could be done in a Handler as a state
- * machine, but it is much easier to read in the current form.
- */
- private class LegacyVpnRunner extends VpnRunner {
- private static final String TAG = "LegacyVpnRunner";
-
- private final String[] mDaemons;
- private final String[][] mArguments;
- private final LocalSocket[] mSockets;
- private final String mOuterInterface;
- private final AtomicInteger mOuterConnection =
- new AtomicInteger(ConnectivityManager.TYPE_NONE);
- private final VpnProfile mProfile;
-
- private long mBringupStartTime = -1;
-
- /**
- * Watch for the outer connection (passing in the constructor) going away.
- */
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mEnableTeardown) return;
-
- if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
- ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
- NetworkInfo info = (NetworkInfo)intent.getExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (info != null && !info.isConnectedOrConnecting()) {
- try {
- mObserver.interfaceStatusChanged(mOuterInterface, false);
- } catch (RemoteException e) {}
- }
- }
- }
- }
- };
-
- // GuardedBy("Vpn.this") (annotation can't be applied to constructor)
- LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
- super(TAG);
- if (racoon == null && mtpd == null) {
- throw new IllegalArgumentException(
- "Arguments to racoon and mtpd must not both be null");
- }
- mConfig = config;
- mDaemons = new String[] {"racoon", "mtpd"};
- // TODO: clear arguments from memory once launched
- mArguments = new String[][] {racoon, mtpd};
- mSockets = new LocalSocket[mDaemons.length];
-
- // This is the interface which VPN is running on,
- // mConfig.interfaze will change to point to OUR
- // internal interface soon. TODO - add inner/outer to mconfig
- // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
- // we will leave the VPN up. We should check that it's still there/connected after
- // registering
- mOuterInterface = mConfig.interfaze;
-
- mProfile = profile;
-
- if (!TextUtils.isEmpty(mOuterInterface)) {
- for (Network network : mConnectivityManager.getAllNetworks()) {
- final LinkProperties lp = mConnectivityManager.getLinkProperties(network);
- if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
- final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network);
- if (netInfo != null) {
- mOuterConnection.set(netInfo.getType());
- break;
- }
- }
- }
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- /**
- * Checks if the parameter matches the underlying interface
- *
- * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has
- * no ability to migrate between interfaces (or Networks).
- */
- public void exitIfOuterInterfaceIs(String interfaze) {
- if (interfaze.equals(mOuterInterface)) {
- Log.i(TAG, "Legacy VPN is going down with " + interfaze);
- exitVpnRunner();
- }
- }
-
- /** Tears down this LegacyVpn connection */
- @Override
- public void exitVpnRunner() {
- // We assume that everything is reset after stopping the daemons.
- interrupt();
-
- // Always disconnect. This may be called again in cleanupVpnStateLocked() if
- // exitVpnRunner() was called from exit(), but it will be a no-op.
- agentDisconnect();
- try {
- mContext.unregisterReceiver(mBroadcastReceiver);
- } catch (IllegalArgumentException e) {}
- }
-
- @Override
- public void run() {
- // Wait for the previous thread since it has been interrupted.
- Log.v(TAG, "Waiting");
- synchronized (TAG) {
- Log.v(TAG, "Executing");
- try {
- bringup();
- waitForDaemonsToStop();
- interrupted(); // Clear interrupt flag if execute called exit.
- } catch (InterruptedException e) {
- } finally {
- for (LocalSocket socket : mSockets) {
- IoUtils.closeQuietly(socket);
- }
- // This sleep is necessary for racoon to successfully complete sending delete
- // message to server.
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- for (String daemon : mDaemons) {
- mDeps.stopService(daemon);
- }
- }
- agentDisconnect();
- }
- }
-
- private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
- long now = SystemClock.elapsedRealtime();
- if (now - mBringupStartTime <= 60000) {
- Thread.sleep(sleepLonger ? 200 : 1);
- } else {
- updateState(DetailedState.FAILED, "checkpoint");
- throw new IllegalStateException("VPN bringup took too long");
- }
- }
-
- private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) {
- final String endpointAddressString = endpointAddress.getHostAddress();
- // Perform some safety checks before inserting the address in place.
- // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd.
- if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) {
- throw new IllegalStateException("Unexpected daemons order");
- }
-
- // Respectively, the positions at which racoon and mtpd take the server address
- // argument are 1 and 2. Not all types of VPN require both daemons however, and
- // in that case the corresponding argument array is null.
- if (mArguments[0] != null) {
- if (!mProfile.server.equals(mArguments[0][1])) {
- throw new IllegalStateException("Invalid server argument for racoon");
- }
- mArguments[0][1] = endpointAddressString;
- }
-
- if (mArguments[1] != null) {
- if (!mProfile.server.equals(mArguments[1][2])) {
- throw new IllegalStateException("Invalid server argument for mtpd");
- }
- mArguments[1][2] = endpointAddressString;
- }
- }
-
- private void bringup() {
- // Catch all exceptions so we can clean up a few things.
- try {
- // resolve never returns null. If it does because of some bug, it will be
- // caught by the catch() block below and cleanup gracefully.
- final InetAddress endpointAddress = mDeps.resolve(mProfile.server);
-
- // Big hack : dynamically replace the address of the server in the arguments
- // with the resolved address.
- checkAndFixupArguments(endpointAddress);
-
- // Initialize the timer.
- mBringupStartTime = SystemClock.elapsedRealtime();
-
- // Wait for the daemons to stop.
- for (String daemon : mDaemons) {
- while (!mDeps.isServiceStopped(daemon)) {
- checkInterruptAndDelay(true);
- }
- }
-
- // Clear the previous state.
- final File state = mDeps.getStateFile();
- state.delete();
- if (state.exists()) {
- throw new IllegalStateException("Cannot delete the state");
- }
- new File("/data/misc/vpn/abort").delete();
-
- updateState(DetailedState.CONNECTING, "execute");
-
- // Start the daemon with arguments.
- for (int i = 0; i < mDaemons.length; ++i) {
- String[] arguments = mArguments[i];
- if (arguments == null) {
- continue;
- }
-
- // Start the daemon.
- String daemon = mDaemons[i];
- mDeps.startService(daemon);
-
- // Wait for the daemon to start.
- while (!mDeps.isServiceRunning(daemon)) {
- checkInterruptAndDelay(true);
- }
-
- // Create the control socket.
- mSockets[i] = new LocalSocket();
-
- // Wait for the socket to connect and send over the arguments.
- mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments,
- this::checkInterruptAndDelay);
- }
-
- // Wait for the daemons to create the new state.
- while (!state.exists()) {
- // Check if a running daemon is dead.
- for (int i = 0; i < mDaemons.length; ++i) {
- String daemon = mDaemons[i];
- if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) {
- throw new IllegalStateException(daemon + " is dead");
- }
- }
- checkInterruptAndDelay(true);
- }
-
- // Now we are connected. Read and parse the new state.
- String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
- if (parameters.length != 7) {
- throw new IllegalStateException("Cannot parse the state: '"
- + String.join("', '", parameters) + "'");
- }
-
- // Set the interface and the addresses in the config.
- synchronized (Vpn.this) {
- mConfig.interfaze = parameters[0].trim();
-
- mConfig.addLegacyAddresses(parameters[1]);
- // Set the routes if they are not set in the config.
- if (mConfig.routes == null || mConfig.routes.isEmpty()) {
- mConfig.addLegacyRoutes(parameters[2]);
- }
-
- // Set the DNS servers if they are not set in the config.
- if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
- String dnsServers = parameters[3].trim();
- if (!dnsServers.isEmpty()) {
- mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
- }
- }
-
- // Set the search domains if they are not set in the config.
- if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
- String searchDomains = parameters[4].trim();
- if (!searchDomains.isEmpty()) {
- mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
- }
- }
-
- // Add a throw route for the VPN server endpoint, if one was specified.
- if (endpointAddress instanceof Inet4Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 32), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else if (endpointAddress instanceof Inet6Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 128), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else {
- Log.e(TAG, "Unknown IP address family for VPN endpoint: "
- + endpointAddress);
- }
-
- // Here is the last step and it must be done synchronously.
- // Set the start time
- mConfig.startTime = SystemClock.elapsedRealtime();
-
- // Check if the thread was interrupted while we were waiting on the lock.
- checkInterruptAndDelay(false);
-
- // Check if the interface is gone while we are waiting.
- if (!mDeps.isInterfacePresent(Vpn.this, mConfig.interfaze)) {
- throw new IllegalStateException(mConfig.interfaze + " is gone");
- }
-
- // Now INetworkManagementEventObserver is watching our back.
- mInterface = mConfig.interfaze;
- prepareStatusIntent();
-
- agentConnect();
-
- Log.i(TAG, "Connected!");
- }
- } catch (Exception e) {
- Log.i(TAG, "Aborting", e);
- updateState(DetailedState.FAILED, e.getMessage());
- exitVpnRunner();
- }
- }
-
- /**
- * Check all daemons every two seconds. Return when one of them is stopped.
- * The caller will move to the disconnected state when this function returns,
- * which can happen if a daemon failed or if the VPN was torn down.
- */
- private void waitForDaemonsToStop() throws InterruptedException {
- if (!mNetworkInfo.isConnected()) {
- return;
- }
- while (true) {
- Thread.sleep(2000);
- for (int i = 0; i < mDaemons.length; i++) {
- if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) {
- return;
- }
- }
- }
- }
- }
-
private void verifyCallingUidAndPackage(String packageName) {
mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
}
@@ -4839,11 +4366,9 @@ public class Vpn {
// Build intent first because the sessionKey will be reset after performing
// VpnRunner.exit(). Also, cache mOwnerUID even if ownerUID will not be changed in
// VpnRunner.exit() to prevent design being changed in the future.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
final int ownerUid = mOwnerUID;
Intent intent = null;
- if (SdkLevel.isAtLeastT() && isVpnApp(mPackage)) {
+ if (isVpnApp(mPackage)) {
intent = buildVpnManagerEventIntent(
VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-1 /* errorClass */, -1 /* errorCode*/, mPackage,
@@ -4884,12 +4409,8 @@ public class Vpn {
// The underlying network, NetworkCapabilities and LinkProperties are not
// necessary to send to VPN app since the purpose of this event is to notify
// VPN app that VPN is deactivated by the user.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- mEventChanges.log("[VMEvent] " + packageName + " stopped");
- sendEventToVpnManagerApp(intent, packageName);
- }
+ mEventChanges.log("[VMEvent] " + packageName + " stopped");
+ sendEventToVpnManagerApp(intent, packageName);
}
private boolean storeAppExclusionList(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 1b48e3ce5946..9f4b3d256b1e 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1058,7 +1058,8 @@ public final class ContentService extends IContentService.Stub {
final long identityToken = clearCallingIdentity();
try {
- return getSyncManager().computeSyncable(account, userId, providerName, false);
+ return getSyncManager().computeSyncable(account, userId, providerName, false,
+ /*checkStoppedState=*/ false);
} finally {
restoreCallingIdentity(identityToken);
}
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 1da7f0c059b0..cd3f0f0ca5b2 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -19,6 +19,7 @@ package com.android.server.content;
import android.annotation.Nullable;
import android.app.job.JobParameters;
import android.app.job.JobService;
+import android.content.pm.PackageManagerInternal;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
@@ -28,6 +29,7 @@ import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
public class SyncJobService extends JobService {
private static final String TAG = "SyncManager";
@@ -97,6 +99,20 @@ public class SyncJobService extends JobService {
return true;
}
+ // TODO(b/209852664): remove this logic from here once it's added within JobScheduler.
+ // JobScheduler should not call onStartJob for syncs whose source packages are stopped.
+ // Until JS adds the relevant logic, this is a temporary solution to keep deferring syncs
+ // for packages in the stopped state.
+ if (android.content.pm.Flags.stayStopped()) {
+ if (LocalServices.getService(PackageManagerInternal.class)
+ .isPackageStopped(op.owningPackage, op.target.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, "Skipping sync for force-stopped package: " + op.owningPackage);
+ }
+ return false;
+ }
+ }
+
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
synchronized (sLock) {
final int jobId = params.getJobId();
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ac7d9c171247..575b30946fce 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -438,6 +438,23 @@ public class SyncManager {
}
};
+ private final BroadcastReceiver mForceStoppedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
+ // For now, just log when packages were force-stopped and unstopped for debugging.
+ if (isLoggable) {
+ if (Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())) {
+ Log.d(TAG, "Package force-stopped: "
+ + intent.getData().getSchemeSpecificPart());
+ } else if (Intent.ACTION_PACKAGE_UNSTOPPED.equals(intent.getAction())) {
+ Log.d(TAG, "Package unstopped: "
+ + intent.getData().getSchemeSpecificPart());
+ }
+ }
+ }
+ };
+
private final HandlerThread mThread;
private final SyncHandler mSyncHandler;
private final SyncManagerConstants mConstants;
@@ -701,6 +718,12 @@ public class SyncManager {
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_UNSTOPPED);
+ intentFilter.addDataScheme("package");
+ context.registerReceiver(mForceStoppedReceiver, intentFilter);
+
intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
context.registerReceiver(mOtherIntentsReceiver, intentFilter);
@@ -1108,7 +1131,7 @@ public class SyncManager {
for (String authority : syncableAuthorities) {
int isSyncable = computeSyncable(account.account, account.userId, authority,
- !checkIfAccountReady);
+ !checkIfAccountReady, /*checkStoppedState=*/ true);
if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
continue;
@@ -1228,7 +1251,7 @@ public class SyncManager {
}
public int computeSyncable(Account account, int userId, String authority,
- boolean checkAccountAccess) {
+ boolean checkAccountAccess, boolean checkStoppedState) {
final int status = getIsSyncable(account, userId, authority);
if (status == AuthorityInfo.NOT_SYNCABLE) {
return AuthorityInfo.NOT_SYNCABLE;
@@ -1241,6 +1264,9 @@ public class SyncManager {
}
final int owningUid = syncAdapterInfo.uid;
final String owningPackage = syncAdapterInfo.componentName.getPackageName();
+ if (checkStoppedState && isPackageStopped(owningPackage, userId)) {
+ return AuthorityInfo.NOT_SYNCABLE;
+ }
if (mAmi.isAppStartModeDisabled(owningUid, owningPackage)) {
Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
+ syncAdapterInfo.componentName
@@ -1256,6 +1282,17 @@ public class SyncManager {
return status;
}
+ /**
+ * Returns whether the package is in a stopped state or not.
+ * Always returns {@code false} if the {@code android.content.pm.stay_stopped} flag is not set.
+ */
+ private boolean isPackageStopped(String packageName, int userId) {
+ if (android.content.pm.Flags.stayStopped()) {
+ return mPackageManagerInternal.isPackageStopped(packageName, userId);
+ }
+ return false;
+ }
+
private boolean canAccessAccount(Account account, String packageName, int uid) {
if (mAccountManager.hasAccountAccess(account, packageName,
UserHandle.getUserHandleForUid(uid))) {
@@ -3496,6 +3533,9 @@ public class SyncManager {
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)
&& op.areExtrasEqual(extras, /*includeSyncSettings=*/ true)) {
+ if (isPackageStopped(op.owningPackage, target.userId)) {
+ continue; // skip stopped package
+ }
maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
return;
}
@@ -3627,7 +3667,8 @@ public class SyncManager {
}
}
// Drop this sync request if it isn't syncable.
- state = computeSyncable(target.account, target.userId, target.provider, true);
+ state = computeSyncable(target.account, target.userId, target.provider, true,
+ /*checkStoppedState=*/ true);
if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: "
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 3de188f08fb1..93d9b8d30a2e 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -411,6 +411,33 @@ final class ColorFade {
}
/**
+ * Destroys ColorFade animation and its resources
+ *
+ * This method should be called when the ColorFade is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ public void destroy() {
+ if (DEBUG) {
+ Slog.d(TAG, "destroy");
+ }
+ if (mPrepared) {
+ if (mCreatedResources) {
+ attachEglContext();
+ try {
+ destroyScreenshotTexture();
+ destroyGLShaders();
+ destroyGLBuffers();
+ destroyEglSurface();
+ } finally {
+ detachEglContext();
+ }
+ }
+ destroyEglContext();
+ destroySurface();
+ }
+ }
+
+ /**
* Draws an animation frame showing the color fade activated at the
* specified level.
*
@@ -793,6 +820,12 @@ final class ColorFade {
}
}
+ private void destroyEglContext() {
+ if (mEglDisplay != null && mEglContext != null) {
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ }
+
private static FloatBuffer createNativeFloatBuffer(int size) {
ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
bb.order(ByteOrder.nativeOrder());
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 669580189d7c..9fcaa1e2af16 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -38,6 +38,7 @@ public final class DisplayBrightnessState {
private final boolean mShouldUseAutoBrightness;
private final boolean mIsSlowChange;
+ private final boolean mShouldUpdateScreenBrightnessSetting;
private final float mCustomAnimationRate;
@@ -50,6 +51,7 @@ public final class DisplayBrightnessState {
mIsSlowChange = builder.isSlowChange();
mMaxBrightness = builder.getMaxBrightness();
mCustomAnimationRate = builder.getCustomAnimationRate();
+ mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
}
/**
@@ -109,6 +111,13 @@ public final class DisplayBrightnessState {
return mCustomAnimationRate;
}
+ /**
+ * @return {@code true} if the screen brightness setting should be updated
+ */
+ public boolean shouldUpdateScreenBrightnessSetting() {
+ return mShouldUpdateScreenBrightnessSetting;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -123,6 +132,8 @@ public final class DisplayBrightnessState {
stringBuilder.append("\n isSlowChange:").append(mIsSlowChange);
stringBuilder.append("\n maxBrightness:").append(mMaxBrightness);
stringBuilder.append("\n customAnimationRate:").append(mCustomAnimationRate);
+ stringBuilder.append("\n shouldUpdateScreenBrightnessSetting:")
+ .append(mShouldUpdateScreenBrightnessSetting);
return stringBuilder.toString();
}
@@ -149,13 +160,16 @@ public final class DisplayBrightnessState {
&& mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
&& mIsSlowChange == otherState.isSlowChange()
&& mMaxBrightness == otherState.getMaxBrightness()
- && mCustomAnimationRate == otherState.getCustomAnimationRate();
+ && mCustomAnimationRate == otherState.getCustomAnimationRate()
+ && mShouldUpdateScreenBrightnessSetting
+ == otherState.shouldUpdateScreenBrightnessSetting();
}
@Override
public int hashCode() {
return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
- mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate);
+ mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate,
+ mShouldUpdateScreenBrightnessSetting);
}
/**
@@ -177,6 +191,7 @@ public final class DisplayBrightnessState {
private boolean mIsSlowChange;
private float mMaxBrightness;
private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+ private boolean mShouldUpdateScreenBrightnessSetting;
/**
* Create a builder starting with the values from the specified {@link
@@ -194,6 +209,8 @@ public final class DisplayBrightnessState {
builder.setIsSlowChange(state.isSlowChange());
builder.setMaxBrightness(state.getMaxBrightness());
builder.setCustomAnimationRate(state.getCustomAnimationRate());
+ builder.setShouldUpdateScreenBrightnessSetting(
+ state.shouldUpdateScreenBrightnessSetting());
return builder;
}
@@ -290,8 +307,8 @@ public final class DisplayBrightnessState {
/**
* See {@link DisplayBrightnessState#isSlowChange()}.
*/
- public Builder setIsSlowChange(boolean shouldUseAutoBrightness) {
- this.mIsSlowChange = shouldUseAutoBrightness;
+ public Builder setIsSlowChange(boolean isSlowChange) {
+ this.mIsSlowChange = isSlowChange;
return this;
}
@@ -334,6 +351,22 @@ public final class DisplayBrightnessState {
}
/**
+ * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}.
+ */
+ public boolean shouldUpdateScreenBrightnessSetting() {
+ return mShouldUpdateScreenBrightnessSetting;
+ }
+
+ /**
+ * See {@link DisplayBrightnessState#shouldUpdateScreenBrightnessSetting()}.
+ */
+ public Builder setShouldUpdateScreenBrightnessSetting(
+ boolean shouldUpdateScreenBrightnessSetting) {
+ mShouldUpdateScreenBrightnessSetting = shouldUpdateScreenBrightnessSetting;
+ return this;
+ }
+
+ /**
* This is used to construct an immutable DisplayBrightnessState object from its builder
*/
public DisplayBrightnessState build() {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9f4f78794659..2fdf90d7d286 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -209,7 +209,7 @@ abstract class DisplayDevice {
int state,
float brightnessState,
float sdrBrightnessState,
- @Nullable DisplayOffloadSession displayOffloadSession) {
+ @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a0beedb1aa64..b99de5cc0c7b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -71,7 +71,7 @@ import com.android.server.display.config.RefreshRateThrottlingPoint;
import com.android.server.display.config.RefreshRateZone;
import com.android.server.display.config.SdrHdrRatioMap;
import com.android.server.display.config.SdrHdrRatioPoint;
-import com.android.server.display.config.SensorDetails;
+import com.android.server.display.config.SensorData;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.ThermalThrottling;
import com.android.server.display.config.ThresholdPoint;
@@ -349,6 +349,20 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <proxSensor>
* <type>android.sensor.proximity</type>
* <name>1234 Proximity Sensor</name>
+ * <refreshRate>
+ * <minimum>60</minimum>
+ * <maximum>60</maximum>
+ * </refreshRate>
+ * <supportedModes>
+ * <point>
+ * <first>60</first> // refreshRate
+ * <second>60</second> //vsyncRate
+ * </point>
+ * <point>
+ * <first>120</first> // refreshRate
+ * <second>120</second> //vsyncRate
+ * </point>
+ * </supportedModes>
* </proxSensor>
*
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
@@ -581,15 +595,15 @@ public class DisplayDeviceConfig {
private final Context mContext;
// The details of the ambient light sensor associated with this display.
- private final SensorData mAmbientLightSensor = new SensorData();
+ private SensorData mAmbientLightSensor;
// The details of the doze brightness sensor associated with this display.
- private final SensorData mScreenOffBrightnessSensor = new SensorData();
+ private SensorData mScreenOffBrightnessSensor;
// The details of the proximity sensor associated with this display.
// Is null when no sensor should be used for that display
@Nullable
- private SensorData mProximitySensor = new SensorData();
+ private SensorData mProximitySensor;
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
@@ -1913,9 +1927,10 @@ public class DisplayDeviceConfig {
loadLuxThrottling(config);
loadQuirks(config);
loadBrightnessRamps(config);
- loadAmbientLightSensorFromDdc(config);
- loadScreenOffBrightnessSensorFromDdc(config);
- loadProxSensorFromDdc(config);
+ mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(config,
+ mContext.getResources());
+ mScreenOffBrightnessSensor = SensorData.loadScreenOffBrightnessSensorConfig(config);
+ mProximitySensor = SensorData.loadProxSensorConfig(config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
loadAutoBrightnessConfigValues(config);
@@ -1940,9 +1955,9 @@ public class DisplayDeviceConfig {
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMapFromConfigXml();
loadBrightnessRampsFromConfigXml();
- loadAmbientLightSensorFromConfigXml();
+ mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
+ mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
loadBrightnessChangeThresholdsFromXml();
- setProxSensorUnspecified();
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
loadRefreshRateSetting(null);
@@ -1966,8 +1981,8 @@ public class DisplayDeviceConfig {
mBrightnessRampDecreaseMaxIdleMillis = 0;
mBrightnessRampIncreaseMaxIdleMillis = 0;
setSimpleMappingStrategyValues();
- loadAmbientLightSensorFromConfigXml();
- setProxSensorUnspecified();
+ mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
+ mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
loadAutoBrightnessAvailableFromConfigXml();
}
@@ -2919,64 +2934,10 @@ public class DisplayDeviceConfig {
mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease;
}
- private void loadAmbientLightSensorFromConfigXml() {
- mAmbientLightSensor.name = "";
- mAmbientLightSensor.type = mContext.getResources().getString(
- com.android.internal.R.string.config_displayLightSensorType);
- }
-
private void loadAutoBrightnessConfigsFromConfigXml() {
loadAutoBrightnessDisplayBrightnessMapping(null /*AutoBrightnessConfig*/);
}
- private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
- final SensorDetails sensorDetails = config.getLightSensor();
- if (sensorDetails != null) {
- loadSensorData(sensorDetails, mAmbientLightSensor);
- } else {
- loadAmbientLightSensorFromConfigXml();
- }
- }
-
- private void setProxSensorUnspecified() {
- mProximitySensor = new SensorData();
- }
-
- private void loadScreenOffBrightnessSensorFromDdc(DisplayConfiguration config) {
- final SensorDetails sensorDetails = config.getScreenOffBrightnessSensor();
- if (sensorDetails != null) {
- loadSensorData(sensorDetails, mScreenOffBrightnessSensor);
- }
- }
-
- private void loadProxSensorFromDdc(DisplayConfiguration config) {
- SensorDetails sensorDetails = config.getProxSensor();
- if (sensorDetails != null) {
- String name = sensorDetails.getName();
- String type = sensorDetails.getType();
- if ("".equals(name) && "".equals(type)) {
- // <proxSensor> with empty values to the config means no sensor should be used
- mProximitySensor = null;
- } else {
- mProximitySensor = new SensorData();
- loadSensorData(sensorDetails, mProximitySensor);
- }
- } else {
- setProxSensorUnspecified();
- }
- }
-
- private void loadSensorData(@NonNull SensorDetails sensorDetails,
- @NonNull SensorData sensorData) {
- sensorData.name = sensorDetails.getName();
- sensorData.type = sensorDetails.getType();
- final RefreshRateRange rr = sensorDetails.getRefreshRate();
- if (rr != null) {
- sensorData.minRefreshRate = rr.getMinimum().floatValue();
- sensorData.maxRefreshRate = rr.getMaximum().floatValue();
- }
- }
-
private void loadBrightnessChangeThresholdsFromXml() {
loadBrightnessChangeThresholds(/* config= */ null);
}
@@ -3390,37 +3351,6 @@ public class DisplayDeviceConfig {
}
/**
- * Uniquely identifies a Sensor, with the combination of Type and Name.
- */
- public static class SensorData {
- public String type;
- public String name;
- public float minRefreshRate = 0.0f;
- public float maxRefreshRate = Float.POSITIVE_INFINITY;
-
- @Override
- public String toString() {
- return "Sensor{"
- + "type: " + type
- + ", name: " + name
- + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]"
- + "} ";
- }
-
- /**
- * @return True if the sensor matches both the specified name and type, or one if only one
- * is specified (not-empty). Always returns false if both parameters are null or empty.
- */
- public boolean matches(String sensorName, String sensorType) {
- final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
- final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
- return (isNameSpecified || isTypeSpecified)
- && (!isNameSpecified || sensorName.equals(name))
- && (!isTypeSpecified || sensorType.equals(type));
- }
- }
-
- /**
* Container for high brightness mode configuration data.
*/
static class HighBrightnessModeData {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 36fc4aa0524d..e99f82aee96d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -102,11 +102,11 @@ import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -158,7 +158,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
-import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
@@ -241,9 +241,6 @@ public final class DisplayManagerService extends SystemService {
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
- @VisibleForTesting
- static final String ENABLE_ON_CONNECT =
- "persist.sys.display.enable_on_connect.external";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
// This value needs to be in sync with the threshold
// in RefreshRateConfigs::getFrameRateDivisor.
@@ -266,6 +263,7 @@ public final class DisplayManagerService extends SystemService {
private final DisplayManagerHandler mHandler;
private final Handler mUiHandler;
private final DisplayModeDirector mDisplayModeDirector;
+ private final ExternalDisplayPolicy mExternalDisplayPolicy;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
@@ -598,6 +596,7 @@ public final class DisplayManagerService extends SystemService {
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
+ mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector());
}
public void setupSchedulerPolicies() {
@@ -667,6 +666,7 @@ public final class DisplayManagerService extends SystemService {
mDisplayModeDirector.onBootCompleted();
mLogicalDisplayMapper.onBootCompleted();
mDisplayNotificationManager.onBootCompleted();
+ mExternalDisplayPolicy.onBootCompleted();
}
}
@@ -1607,6 +1607,19 @@ public final class DisplayManagerService extends SystemService {
final long secondToken = Binder.clearCallingIdentity();
try {
final int displayId;
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ packageName, callingUid, virtualDisplayConfig);
+
+ if (virtualDisplayConfig.isHomeSupported()) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ Slog.w(TAG, "Display created with home support but lacks "
+ + "VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the home support request.");
+ } else {
+ mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
+ Display.TYPE_VIRTUAL, true);
+ }
+ }
+
synchronized (mSyncRoot) {
displayId =
createVirtualDisplayLocked(
@@ -1614,6 +1627,7 @@ public final class DisplayManagerService extends SystemService {
projection,
callingUid,
packageName,
+ displayUniqueId,
virtualDevice,
surface,
flags,
@@ -1625,6 +1639,13 @@ public final class DisplayManagerService extends SystemService {
}
}
+ if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported()
+ && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+ // Failed to create the virtual display, so we should clean up the WM settings
+ // because it won't receive the onDisplayRemoved callback.
+ mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL);
+ }
+
// Build a session describing the MediaProjection instance, if there is one. A session
// for a VirtualDisplay or physical display mirroring is handled in DisplayContent.
ContentRecordingSession session = null;
@@ -1698,6 +1719,7 @@ public final class DisplayManagerService extends SystemService {
IMediaProjection projection,
int callingUid,
String packageName,
+ String uniqueId,
IVirtualDevice virtualDevice,
Surface surface,
int flags,
@@ -1710,10 +1732,9 @@ public final class DisplayManagerService extends SystemService {
return -1;
}
-
Slog.d(TAG, "Virtual Display: creating DisplayDevice with VirtualDisplayAdapter");
DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
- callback, projection, callingUid, packageName, surface, flags,
+ callback, projection, callingUid, packageName, uniqueId, surface, flags,
virtualDisplayConfig);
if (device == null) {
Slog.w(TAG, "Virtual Display: VirtualDisplayAdapter failed to create DisplayDevice");
@@ -1947,17 +1968,12 @@ public final class DisplayManagerService extends SystemService {
}
setupLogicalDisplay(display);
- // TODO(b/292196201) Remove when the display can be disabled before DPC is created.
- if (display.getDisplayInfoLocked().type == Display.TYPE_EXTERNAL) {
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
- Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
- } else {
- display.setEnabledLocked(false);
- }
- }
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED);
+ if (ExternalDisplayPolicy.isExternalDisplay(display)) {
+ mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(display);
+ } else {
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED);
+ }
updateLogicalDisplayState(display);
}
@@ -3248,7 +3264,14 @@ public final class DisplayManagerService extends SystemService {
void enableConnectedDisplay(int displayId, boolean enabled) {
synchronized (mSyncRoot) {
- mLogicalDisplayMapper.setDisplayEnabledLocked(displayId, enabled);
+ final var logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (logicalDisplay == null) {
+ Slog.w(TAG, "enableConnectedDisplay: Can not find displayId=" + displayId);
+ } else if (ExternalDisplayPolicy.isExternalDisplay(logicalDisplay)) {
+ mExternalDisplayPolicy.setExternalDisplayEnabledLocked(logicalDisplay, enabled);
+ } else {
+ mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, enabled);
+ }
}
}
@@ -4470,22 +4493,12 @@ public final class DisplayManagerService extends SystemService {
@EnforcePermission(MANAGE_DISPLAYS)
public void enableConnectedDisplay(int displayId) {
enableConnectedDisplay_enforcePermission();
- if (!mFlags.isConnectedDisplayManagementEnabled()) {
- Slog.w(TAG, "External display management is not enabled on your device: "
- + "cannot enable display.");
- return;
- }
DisplayManagerService.this.enableConnectedDisplay(displayId, true);
}
@EnforcePermission(MANAGE_DISPLAYS)
public void disableConnectedDisplay(int displayId) {
disableConnectedDisplay_enforcePermission();
- if (!mFlags.isConnectedDisplayManagementEnabled()) {
- Slog.w(TAG, "External display management is not enabled on your device: "
- + "cannot disable display.");
- return;
- }
DisplayManagerService.this.enableConnectedDisplay(displayId, false);
}
}
@@ -4935,20 +4948,8 @@ public final class DisplayManagerService extends SystemService {
return null;
}
- DisplayOffloadSession session =
- new DisplayOffloadSession() {
- @Override
- public void setDozeStateOverride(int displayState) {
- synchronized (mSyncRoot) {
- displayPowerController.overrideDozeScreenState(displayState);
- }
- }
-
- @Override
- public DisplayOffloader getDisplayOffloader() {
- return displayOffloader;
- }
- };
+ DisplayOffloadSessionImpl session = new DisplayOffloadSessionImpl(displayOffloader,
+ displayPowerController);
logicalDisplay.setDisplayOffloadSessionLocked(session);
displayPowerController.setDisplayOffloadSession(session);
return session;
@@ -5043,4 +5044,73 @@ public final class DisplayManagerService extends SystemService {
*/
long uptimeMillis();
}
+
+ /**
+ * Implements necessary functionality for {@link ExternalDisplayPolicy}
+ */
+ private class ExternalDisplayPolicyInjector implements ExternalDisplayPolicy.Injector {
+ /**
+ * Sends event for the display.
+ */
+ @Override
+ public void sendExternalDisplayEventLocked(@NonNull final LogicalDisplay display,
+ @DisplayEvent int event) {
+ sendDisplayEventLocked(display, event);
+ }
+
+ /**
+ * Gets thermal service
+ */
+ @Override
+ @Nullable
+ public IThermalService getThermalService() {
+ return IThermalService.Stub.asInterface(ServiceManager.getService(
+ Context.THERMAL_SERVICE));
+ }
+
+ /**
+ * @return display manager flags.
+ */
+ @Override
+ @NonNull
+ public DisplayManagerFlags getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * @return Logical display mapper.
+ */
+ @Override
+ @NonNull
+ public LogicalDisplayMapper getLogicalDisplayMapper() {
+ return mLogicalDisplayMapper;
+ }
+
+ /**
+ * @return Sync root, for synchronization on this object across display manager.
+ */
+ @Override
+ @NonNull
+ public SyncRoot getSyncRoot() {
+ return mSyncRoot;
+ }
+
+ /**
+ * Notification manager for display manager
+ */
+ @Override
+ @NonNull
+ public DisplayNotificationManager getDisplayNotificationManager() {
+ return mDisplayNotificationManager;
+ }
+
+ /**
+ * Handler to use for notification sending to avoid requiring POST_NOTIFICATION permission.
+ */
+ @Override
+ @NonNull
+ public Handler getHandler() {
+ return mHandler;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
new file mode 100644
index 000000000000..1bd556bdcc4f
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+import android.os.Trace;
+
+/**
+ * An implementation of the offload session that keeps track of whether the session is active.
+ * An offload session is used to control the display's brightness using the offload chip.
+ */
+public class DisplayOffloadSessionImpl implements DisplayManagerInternal.DisplayOffloadSession {
+
+ @Nullable
+ private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+ private final DisplayPowerControllerInterface mDisplayPowerController;
+ private boolean mIsActive;
+
+ public DisplayOffloadSessionImpl(
+ @Nullable DisplayManagerInternal.DisplayOffloader displayOffloader,
+ DisplayPowerControllerInterface displayPowerController) {
+ mDisplayOffloader = displayOffloader;
+ mDisplayPowerController = displayPowerController;
+ }
+
+ @Override
+ public void setDozeStateOverride(int displayState) {
+ mDisplayPowerController.overrideDozeScreenState(displayState);
+ }
+
+ @Override
+ public boolean isActive() {
+ return mIsActive;
+ }
+
+ @Override
+ public void updateBrightness(float brightness) {
+ if (mIsActive) {
+ mDisplayPowerController.setBrightnessFromOffload(brightness);
+ }
+ }
+
+ /**
+ * Start the offload session. The method returns if the session is already active.
+ * @return Whether the session was started successfully
+ */
+ public boolean startOffload() {
+ if (mDisplayOffloader == null || mIsActive) {
+ return false;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
+ try {
+ return mIsActive = mDisplayOffloader.startOffload();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
+
+ /**
+ * Stop the offload session. The method returns if the session is not active.
+ */
+ public void stopOffload() {
+ if (mDisplayOffloader == null || !mIsActive) {
+ return;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#stopOffload");
+ try {
+ mDisplayOffloader.stopOffload();
+ mIsActive = false;
+ mDisplayPowerController.setBrightnessFromOffload(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d8ac52eb8c79..f3d761a7372b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1540,12 +1540,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
performScreenOffTransition = true;
break;
case DisplayPowerRequest.POLICY_DOZE:
- if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+ if (mDozeStateOverride != Display.STATE_UNKNOWN) {
+ state = mDozeStateOverride;
+ } else if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
state = mPowerRequest.dozeScreenState;
} else {
state = Display.STATE_DOZE;
}
- state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
if (!mAllowAutoBrightnessWhileDozingConfig) {
brightnessState = mPowerRequest.dozeScreenBrightness;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
@@ -2223,6 +2224,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
@Override
+ public void setBrightnessFromOffload(float brightness) {
+ // The old DPC is no longer supported
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index a6155da86f9a..d4e0cbb126ed 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -151,6 +151,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private static final int MSG_SET_DWBC_STRONG_MODE = 14;
private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
+ private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
@@ -562,7 +563,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
new DisplayBrightnessController(context, null,
mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
brightnessSetting, () -> postBrightnessChangeRunnable(),
- new HandlerExecutor(mHandler));
+ new HandlerExecutor(mHandler), flags);
mBrightnessClamperController = mInjector.getBrightnessClamperController(
mHandler, modeChangeCallback::run,
@@ -1394,7 +1395,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
- boolean updateScreenBrightnessSetting = false;
+ boolean updateScreenBrightnessSetting =
+ displayBrightnessState.shouldUpdateScreenBrightnessSetting();
float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
// Apply auto-brightness.
int brightnessAdjustmentFlags = 0;
@@ -1854,6 +1856,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
@Override
+ public void setBrightnessFromOffload(float brightness) {
+ Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
+ Float.floatToIntBits(brightness), 0 /*unused*/);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -2886,6 +2895,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
case MSG_SET_DWBC_LOGGING_ENABLED:
setDwbcLoggingEnabled(msg.arg1);
break;
+ case MSG_SET_BRIGHTNESS_FROM_OFFLOAD:
+ mDisplayBrightnessController.setBrightnessFromOffload(
+ Float.intBitsToFloat(msg.arg1));
+ updatePowerState();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 181386a93c71..72079a4c82fe 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -22,6 +22,7 @@ import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
import java.io.PrintWriter;
@@ -150,6 +151,14 @@ public interface DisplayPowerControllerInterface {
void setTemporaryAutoBrightnessAdjustment(float adjustment);
/**
+ * Sets temporary brightness from the offload chip until we get a brightness value from
+ * the light sensor.
+ * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
+ * {@link PowerManager.BRIGHTNESS_MAX}. Values outside of that range will be ignored.
+ */
+ void setBrightnessFromOffload(float brightness);
+
+ /**
* Gets the screen brightness setting
*/
float getScreenBrightnessSetting();
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index be03a808e166..f994c0556c82 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -320,7 +320,9 @@ final class DisplayPowerState {
public void stop() {
mStopped = true;
mPhotonicModulator.interrupt();
- dismissColorFade();
+ if (mColorFade != null) {
+ mColorFade.destroy();
+ }
mCleanListener = null;
mHandler.removeCallbacksAndMessages(null);
}
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
new file mode 100644
index 000000000000..dbe1e14f683f
--- /dev/null
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static android.hardware.display.DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED;
+import static android.os.Temperature.THROTTLING_CRITICAL;
+import static android.os.Temperature.THROTTLING_NONE;
+import static android.view.Display.TYPE_EXTERNAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerGlobal.DisplayEvent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Temperature;
+import android.os.Temperature.ThrottlingStatus;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.notifications.DisplayNotificationManager;
+import com.android.server.display.utils.DebugUtils;
+
+/**
+ * Listens for Skin thermal sensor events, disables external displays if thermal status becomes
+ * equal or above {@link android.os.Temperature#THROTTLING_CRITICAL}, enables external displays if
+ * status goes below {@link android.os.Temperature#THROTTLING_CRITICAL}.
+ */
+class ExternalDisplayPolicy {
+ private static final String TAG = "ExternalDisplayPolicy";
+
+ // To enable these logs, run:
+ // 'adb shell setprop persist.log.tag.ExternalDisplayPolicy DEBUG && adb reboot'
+ private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
+ @VisibleForTesting
+ static final String ENABLE_ON_CONNECT = "persist.sys.display.enable_on_connect.external";
+
+ static boolean isExternalDisplay(@NonNull final LogicalDisplay logicalDisplay) {
+ return logicalDisplay.getDisplayInfoLocked().type == TYPE_EXTERNAL;
+ }
+
+ /**
+ * Injector interface for {@link ExternalDisplayPolicy}
+ */
+ interface Injector {
+ void sendExternalDisplayEventLocked(@NonNull LogicalDisplay display,
+ @DisplayEvent int event);
+
+ @NonNull
+ LogicalDisplayMapper getLogicalDisplayMapper();
+
+ @NonNull
+ SyncRoot getSyncRoot();
+
+ @Nullable
+ IThermalService getThermalService();
+
+ @NonNull
+ DisplayManagerFlags getFlags();
+
+ @NonNull
+ DisplayNotificationManager getDisplayNotificationManager();
+
+ @NonNull
+ Handler getHandler();
+ }
+
+ @NonNull
+ private final Injector mInjector;
+ @NonNull
+ private final LogicalDisplayMapper mLogicalDisplayMapper;
+ @NonNull
+ private final SyncRoot mSyncRoot;
+ @NonNull
+ private final DisplayManagerFlags mFlags;
+ @NonNull
+ private final DisplayNotificationManager mDisplayNotificationManager;
+ @NonNull
+ private final Handler mHandler;
+ @ThrottlingStatus
+ private volatile int mStatus = THROTTLING_NONE;
+
+ ExternalDisplayPolicy(@NonNull final Injector injector) {
+ mInjector = injector;
+ mLogicalDisplayMapper = mInjector.getLogicalDisplayMapper();
+ mSyncRoot = mInjector.getSyncRoot();
+ mFlags = mInjector.getFlags();
+ mDisplayNotificationManager = mInjector.getDisplayNotificationManager();
+ mHandler = mInjector.getHandler();
+ }
+
+ /**
+ * Starts listening for temperature changes.
+ */
+ void onBootCompleted() {
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ if (DEBUG) {
+ Slog.d(TAG, "External display management is not enabled on your device:"
+ + " cannot register thermal listener.");
+ }
+ return;
+ }
+
+ if (!mFlags.isConnectedDisplayErrorHandlingEnabled()) {
+ if (DEBUG) {
+ Slog.d(TAG, "ConnectedDisplayErrorHandlingEnabled is not enabled on your device:"
+ + " cannot register thermal listener.");
+ }
+ return;
+ }
+
+ if (!registerThermalServiceListener(new SkinThermalStatusObserver())) {
+ Slog.e(TAG, "Failed to register thermal listener");
+ }
+ }
+
+ /**
+ * Checks the display type is external, and if it is external then enables/disables it.
+ */
+ void setExternalDisplayEnabledLocked(@NonNull final LogicalDisplay logicalDisplay,
+ final boolean enabled) {
+ if (!isExternalDisplay(logicalDisplay)) {
+ Slog.e(TAG, "setExternalDisplayEnabledLocked called for non external display");
+ return;
+ }
+
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ if (DEBUG) {
+ Slog.d(TAG, "setExternalDisplayEnabledLocked: External display management is not"
+ + " enabled on your device, cannot enable/disable display.");
+ }
+ return;
+ }
+
+ if (enabled && !isExternalDisplayAllowed()) {
+ Slog.w(TAG, "setExternalDisplayEnabledLocked: External display can not be enabled"
+ + " because it is currently not allowed.");
+ mHandler.post(mDisplayNotificationManager::onHighTemperatureExternalDisplayNotAllowed);
+ return;
+ }
+
+ mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, enabled);
+ }
+
+ /**
+ * Upon external display became available check if external displays allowed, this display
+ * is disabled and then sends {@link DisplayManagerGlobal#EVENT_DISPLAY_CONNECTED} to allow
+ * user to decide how to use this display.
+ */
+ void handleExternalDisplayConnectedLocked(@NonNull final LogicalDisplay logicalDisplay) {
+ if (!isExternalDisplay(logicalDisplay)) {
+ Slog.e(TAG, "handleExternalDisplayConnectedLocked called for non-external display");
+ return;
+ }
+
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ if (DEBUG) {
+ Slog.d(TAG, "handleExternalDisplayConnectedLocked connected display management"
+ + " flag is off");
+ }
+ return;
+ }
+
+ if ((Build.IS_ENG || Build.IS_USERDEBUG)
+ && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
+ Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
+ mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED);
+ return;
+ } else {
+ // As external display is enabled by default, need to disable it now.
+ // TODO(b/292196201) Remove when the display can be disabled before DPC is created.
+ logicalDisplay.setEnabledLocked(false);
+ }
+
+ if (!isExternalDisplayAllowed()) {
+ Slog.w(TAG, "handleExternalDisplayConnectedLocked: External display can not be used"
+ + " because it is currently not allowed.");
+ mDisplayNotificationManager.onHighTemperatureExternalDisplayNotAllowed();
+ return;
+ }
+
+ mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED);
+
+ if (DEBUG) {
+ Slog.d(TAG, "handleExternalDisplayConnectedLocked complete"
+ + " displayId=" + logicalDisplay.getDisplayIdLocked());
+ }
+ }
+
+ @GuardedBy("mSyncRoot")
+ private void disableExternalDisplayLocked(@NonNull final LogicalDisplay logicalDisplay) {
+ if (!isExternalDisplay(logicalDisplay)) {
+ return;
+ }
+
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ Slog.e(TAG, "disableExternalDisplayLocked shouldn't be called when the"
+ + " connected display management flag is off");
+ return;
+ }
+
+ if (!mFlags.isConnectedDisplayErrorHandlingEnabled()) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableExternalDisplayLocked shouldn't be called when the"
+ + " error handling flag is off");
+ }
+ return;
+ }
+
+ if (!logicalDisplay.isEnabledLocked()) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableExternalDisplayLocked is not allowed:"
+ + " displayId=" + logicalDisplay.getDisplayIdLocked()
+ + " isEnabledLocked=false");
+ }
+ return;
+ }
+
+ if (!isExternalDisplayAllowed()) {
+ Slog.w(TAG, "External display is currently not allowed and is getting disabled.");
+ mDisplayNotificationManager.onHighTemperatureExternalDisplayNotAllowed();
+ }
+
+ mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, /*enabled=*/ false);
+
+ if (DEBUG) {
+ Slog.d(TAG, "disableExternalDisplayLocked complete"
+ + " displayId=" + logicalDisplay.getDisplayIdLocked());
+ }
+ }
+
+ /**
+ * @return whether external displays use is currently allowed.
+ */
+ @VisibleForTesting
+ boolean isExternalDisplayAllowed() {
+ return mStatus < THROTTLING_CRITICAL;
+ }
+
+ private boolean registerThermalServiceListener(
+ @NonNull final IThermalEventListener.Stub listener) {
+ final var thermalService = mInjector.getThermalService();
+ if (thermalService == null) {
+ Slog.w(TAG, "Could not observe thermal status. Service not available");
+ return false;
+ }
+ try {
+ thermalService.registerThermalEventListenerWithType(listener, Temperature.TYPE_SKIN);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ return false;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "registerThermalServiceListener complete.");
+ }
+ return true;
+ }
+
+ private void disableExternalDisplays() {
+ synchronized (mSyncRoot) {
+ mLogicalDisplayMapper.forEachLocked(this::disableExternalDisplayLocked);
+ }
+ }
+
+ private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
+ @Override
+ public void notifyThrottling(@NonNull final Temperature temp) {
+ @ThrottlingStatus final int newStatus = temp.getStatus();
+ final var previousStatus = mStatus;
+ mStatus = newStatus;
+ if (THROTTLING_CRITICAL > previousStatus && THROTTLING_CRITICAL <= newStatus) {
+ disableExternalDisplays();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index be3207dfb4ee..ff9a1ab61b13 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -19,11 +19,11 @@ package com.android.server.display;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.Mode.INVALID_MODE_ID;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -238,7 +238,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private boolean mAllmRequested;
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
- private boolean mDisplayOffloadActive;
private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
// The supported display modes according to SurfaceFlinger
private SurfaceControl.DisplayMode[] mSfDisplayModes;
@@ -765,7 +764,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
final int state,
final float brightnessState,
final float sdrBrightnessState,
- DisplayOffloadSession displayOffloadSession) {
+ @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
// Assume that the brightness is off if the display is being turned off.
assert state != Display.STATE_OFF
@@ -832,25 +831,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
+ ", state=" + Display.stateToString(state) + ")");
}
- DisplayOffloader displayOffloader =
- displayOffloadSession == null
- ? null
- : displayOffloadSession.getDisplayOffloader();
-
boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
if (isDisplayOffloadEnabled) {
- if (mDisplayOffloadActive && displayOffloader != null) {
- Trace.traceBegin(Trace.TRACE_TAG_POWER,
- "DisplayOffloader#stopOffload");
- try {
- displayOffloader.stopOffload();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
- }
- mDisplayOffloadActive = false;
+ if (displayOffloadSession != null) {
+ displayOffloadSession.stopOffload();
}
} else {
if (mSidekickActive) {
@@ -881,16 +868,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// have a sidekick/displayoffload available, tell it now that it can take
// control.
if (isDisplayOffloadEnabled) {
- if (DisplayOffloadSession.isSupportedOffloadState(state) &&
- displayOffloader != null
- && !mDisplayOffloadActive) {
- Trace.traceBegin(
- Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
- try {
- mDisplayOffloadActive = displayOffloader.startOffload();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
- }
+ if (DisplayOffloadSession.isSupportedOffloadState(state)
+ && displayOffloadSession != null) {
+ displayOffloadSession.startOffload();
}
} else {
if (Display.isSuspendedState(state) && state != Display.STATE_OFF
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 3d4209e0d6f3..ba321ae5d807 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -138,7 +138,7 @@ final class LogicalDisplay {
private final Rect mTempDisplayRect = new Rect();
/** A session token that controls the offloading operations of this logical display. */
- private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+ private DisplayOffloadSessionImpl mDisplayOffloadSession;
/**
* Name of a display group to which the display is assigned.
@@ -969,12 +969,11 @@ final class LogicalDisplay {
return mDisplayGroupName;
}
- public void setDisplayOffloadSessionLocked(
- DisplayManagerInternal.DisplayOffloadSession session) {
+ public void setDisplayOffloadSessionLocked(DisplayOffloadSessionImpl session) {
mDisplayOffloadSession = session;
}
- public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() {
+ public DisplayOffloadSessionImpl getDisplayOffloadSessionLocked() {
return mDisplayOffloadSession;
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f3425d2e2136..115111a4afa3 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -1255,16 +1255,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return null;
}
- void setDisplayEnabledLocked(int displayId, boolean enabled) {
- LogicalDisplay display = getDisplayLocked(displayId);
- if (display == null) {
- Slog.w(TAG, "Cannot find display " + displayId);
- return;
- }
+ void setDisplayEnabledLocked(@NonNull LogicalDisplay display, boolean enabled) {
boolean isEnabled = display.isEnabledLocked();
if (isEnabled == enabled) {
Slog.w(TAG, "Display is already " + (isEnabled ? "enabled" : "disabled") + ": "
- + displayId);
+ + display.getDisplayIdLocked());
return;
}
setEnabledLocked(display, enabled);
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index b0025872aa3d..edbd42465534 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -43,7 +43,6 @@ import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
@@ -64,7 +63,7 @@ import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A display adapter that provides virtual displays on behalf of applications.
@@ -72,15 +71,17 @@ import java.util.Iterator;
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class VirtualDisplayAdapter extends DisplayAdapter {
static final String TAG = "VirtualDisplayAdapter";
- static final boolean DEBUG = false;
// Unique id prefix for virtual displays
@VisibleForTesting
static final String UNIQUE_ID_PREFIX = "virtual:";
+ // Unique id suffix for virtual displays
+ private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0);
+
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
private final Handler mHandler;
private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
@@ -111,8 +112,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig) {
+ IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId,
+ Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) {
IBinder appToken = callback.asBinder();
if (mVirtualDisplayDevices.containsKey(appToken)) {
Slog.wtfStack(TAG,
@@ -125,23 +126,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure,
virtualDisplayConfig.getRequestedRefreshRate());
- final String baseUniqueId =
- UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
- final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
- String uniqueId = virtualDisplayConfig.getUniqueId();
- if (uniqueId == null) {
- uniqueId = baseUniqueId + uniqueIndex;
- } else {
- uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
- }
MediaProjectionCallback mediaProjectionCallback = null;
if (projection != null) {
mediaProjectionCallback = new MediaProjectionCallback(appToken);
}
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, surface, flags,
- new Callback(callback, mHandler), projection, mediaProjectionCallback,
- uniqueId, uniqueIndex, virtualDisplayConfig);
+ ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
@@ -219,26 +210,20 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
/**
- * Returns the next unique index for the uniqueIdPrefix
+ * Generates a virtual display's unique identifier.
+ *
+ * <p>It is always prefixed with "virtual:package-name". If the provided config explicitly
+ * specifies a unique ID, then it's simply appended. Otherwise, the UID, display name and a
+ * unique index are appended.</p>
+ *
+ * <p>The unique index is incremented for every virtual display unique ID generation and serves
+ * for differentiating between displays with the same name created by the same owner.</p>
*/
- private int getNextUniqueIndex(String uniqueIdPrefix) {
- if (mVirtualDisplayDevices.isEmpty()) {
- return 0;
- }
-
- int nextUniqueIndex = 0;
- Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
- while (it.hasNext()) {
- VirtualDisplayDevice device = it.next();
- if (device.getUniqueId().startsWith(uniqueIdPrefix)
- && device.mUniqueIndex >= nextUniqueIndex) {
- // Increment the next unique index to be greater than ones we have already ran
- // across for displays that have the same unique Id prefix.
- nextUniqueIndex = device.mUniqueIndex + 1;
- }
- }
-
- return nextUniqueIndex;
+ static String generateDisplayUniqueId(String packageName, int uid,
+ VirtualDisplayConfig config) {
+ return UNIQUE_ID_PREFIX + packageName + ((config.getUniqueId() != null)
+ ? (":" + config.getUniqueId())
+ : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement()));
}
private void handleBinderDiedLocked(IBinder appToken) {
@@ -278,7 +263,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private int mDisplayState;
private boolean mStopped;
private int mPendingChanges;
- private int mUniqueIndex;
private Display.Mode mMode;
private boolean mIsDisplayOn;
private int mDisplayIdToMirror;
@@ -287,7 +271,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
Callback callback, IMediaProjection projection,
- IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
+ IMediaProjectionCallback mediaProjectionCallback, String uniqueId,
VirtualDisplayConfig virtualDisplayConfig) {
super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
mAppToken = appToken;
@@ -306,7 +290,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mMediaProjectionCallback = mediaProjectionCallback;
mDisplayState = Display.STATE_UNKNOWN;
mPendingChanges |= PENDING_SURFACE_CHANGE;
- mUniqueIndex = uniqueIndex;
mIsDisplayOn = surface != null;
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
@@ -396,7 +379,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
@Override
public Runnable requestDisplayStateLocked(int state, float brightnessState,
- float sdrBrightnessState, DisplayOffloadSession displayOffloadSession) {
+ float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) {
if (state != mDisplayState) {
mDisplayState = state;
if (state == Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index d7ae2699ee2d..8fe5f213d766 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -39,7 +39,8 @@ public final class BrightnessReason {
public static final int REASON_BOOST = 8;
public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
public static final int REASON_FOLLOWER = 10;
- public static final int REASON_MAX = REASON_FOLLOWER;
+ public static final int REASON_OFFLOAD = 11;
+ public static final int REASON_MAX = REASON_OFFLOAD;
public static final int MODIFIER_DIMMED = 0x1;
public static final int MODIFIER_LOW_POWER = 0x2;
@@ -196,6 +197,8 @@ public final class BrightnessReason {
return "screen_off_brightness_sensor";
case REASON_FOLLOWER:
return "follower";
+ case REASON_OFFLOAD:
+ return "offload";
default:
return Integer.toString(reason);
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index d6f0098c13cb..617befbbd17d 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -31,6 +31,7 @@ import com.android.server.display.BrightnessSetting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -104,7 +105,8 @@ public final class DisplayBrightnessController {
*/
public DisplayBrightnessController(Context context, Injector injector, int displayId,
float defaultScreenBrightness, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor) {
+ Runnable onBrightnessChangeRunnable, HandlerExecutor brightnessChangeExecutor,
+ DisplayManagerFlags flags) {
if (injector == null) {
injector = new Injector();
}
@@ -116,7 +118,7 @@ public final class DisplayBrightnessController {
mCurrentScreenBrightness = getScreenBrightnessSetting();
mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context,
- displayId);
+ displayId, flags);
mBrightnessChangeExecutor = brightnessChangeExecutor;
mPersistBrightnessNitsForDefaultDisplay = context.getResources().getBoolean(
com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay);
@@ -172,6 +174,18 @@ public final class DisplayBrightnessController {
}
/**
+ * Sets the brightness from the offload session.
+ */
+ public void setBrightnessFromOffload(float brightness) {
+ synchronized (mLock) {
+ if (mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() != null) {
+ mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()
+ .setOffloadScreenBrightness(brightness);
+ }
+ }
+ }
+
+ /**
* Returns a boolean flag indicating if the light sensor is to be used to decide the screen
* brightness when dozing
*/
@@ -423,8 +437,9 @@ public final class DisplayBrightnessController {
@VisibleForTesting
static class Injector {
DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(Context context,
- int displayId) {
- return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId);
+ int displayId, DisplayManagerFlags flags) {
+ return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId,
+ flags);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index f141c20158cd..055f94a23363 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
import android.util.IndentingPrintWriter;
@@ -31,9 +32,11 @@ import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -63,6 +66,10 @@ public class DisplayBrightnessStrategySelector {
private final InvalidBrightnessStrategy mInvalidBrightnessStrategy;
// Controls brightness when automatic (adaptive) brightness is running.
private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
+ // Controls the brightness if adaptive brightness is on and there exists an active offload
+ // session. Brightness value is provided by the offload session.
+ @Nullable
+ private final OffloadBrightnessStrategy mOffloadBrightnessStrategy;
// We take note of the old brightness strategy so that we can know when the strategy changes.
private String mOldBrightnessStrategyName;
@@ -72,7 +79,8 @@ public class DisplayBrightnessStrategySelector {
/**
* The constructor of DozeBrightnessStrategy.
*/
- public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId) {
+ public DisplayBrightnessStrategySelector(Context context, Injector injector, int displayId,
+ DisplayManagerFlags flags) {
if (injector == null) {
injector = new Injector();
}
@@ -85,6 +93,11 @@ public class DisplayBrightnessStrategySelector {
mFollowerBrightnessStrategy = injector.getFollowerBrightnessStrategy(displayId);
mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
mAutomaticBrightnessStrategy = injector.getAutomaticBrightnessStrategy(context, displayId);
+ if (flags.isDisplayOffloadEnabled()) {
+ mOffloadBrightnessStrategy = injector.getOffloadBrightnessStrategy();
+ } else {
+ mOffloadBrightnessStrategy = null;
+ }
mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
R.bool.config_allowAutoBrightnessWhileDozing);
mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName();
@@ -114,6 +127,9 @@ public class DisplayBrightnessStrategySelector {
} else if (BrightnessUtils.isValidBrightnessValue(
mTemporaryBrightnessStrategy.getTemporaryScreenBrightness())) {
displayBrightnessStrategy = mTemporaryBrightnessStrategy;
+ } else if (mOffloadBrightnessStrategy != null && BrightnessUtils.isValidBrightnessValue(
+ mOffloadBrightnessStrategy.getOffloadScreenBrightness())) {
+ displayBrightnessStrategy = mOffloadBrightnessStrategy;
}
if (!mOldBrightnessStrategyName.equals(displayBrightnessStrategy.getName())) {
@@ -138,6 +154,11 @@ public class DisplayBrightnessStrategySelector {
return mAutomaticBrightnessStrategy;
}
+ @Nullable
+ public OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+ return mOffloadBrightnessStrategy;
+ }
+
/**
* Returns a boolean flag indicating if the light sensor is to be used to decide the screen
* brightness when dozing
@@ -159,6 +180,9 @@ public class DisplayBrightnessStrategySelector {
+ mAllowAutoBrightnessWhileDozingConfig);
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
mTemporaryBrightnessStrategy.dump(ipw);
+ if (mOffloadBrightnessStrategy != null) {
+ mOffloadBrightnessStrategy.dump(ipw);
+ }
}
/**
@@ -210,5 +234,9 @@ public class DisplayBrightnessStrategySelector {
AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context, int displayId) {
return new AutomaticBrightnessStrategy(context, displayId);
}
+
+ OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+ return new OffloadBrightnessStrategy();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index bcd52598edd2..3c23b5c10671 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -107,6 +107,7 @@ public class AutomaticBrightnessStrategy {
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
&& (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
+ && brightnessReason != BrightnessReason.REASON_OFFLOAD
&& mAutomaticBrightnessController != null;
mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
&& !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze);
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
new file mode 100644
index 000000000000..55f8914e26f6
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of the display when auto-brightness is on, the screen has just turned on
+ * and there is no available lux reading yet. The brightness value is read from the offload chip.
+ */
+public class OffloadBrightnessStrategy implements DisplayBrightnessStrategy {
+
+ private float mOffloadScreenBrightness;
+
+ public OffloadBrightnessStrategy() {
+ mOffloadScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ @Override
+ public DisplayBrightnessState updateBrightness(
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD);
+ return new DisplayBrightnessState.Builder()
+ .setBrightness(mOffloadScreenBrightness)
+ .setSdrBrightness(mOffloadScreenBrightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(getName())
+ .setIsSlowChange(false)
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .build();
+ }
+
+ @Override
+ public String getName() {
+ return "OffloadBrightnessStrategy";
+ }
+
+ public float getOffloadScreenBrightness() {
+ return mOffloadScreenBrightness;
+ }
+
+ public void setOffloadScreenBrightness(float offloadScreenBrightness) {
+ mOffloadScreenBrightness = offloadScreenBrightness;
+ }
+
+ /**
+ * Dumps the state of this class.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println("OffloadBrightnessStrategy:");
+ writer.println(" mOffloadScreenBrightness:" + mOffloadScreenBrightness);
+ }
+}
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
new file mode 100644
index 000000000000..3bb35bf7c49f
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.config;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Uniquely identifies a Sensor, with the combination of Type and Name.
+ */
+public class SensorData {
+
+ @Nullable
+ public final String type;
+ @Nullable
+ public final String name;
+ public final float minRefreshRate;
+ public final float maxRefreshRate;
+ public final List<SupportedMode> supportedModes;
+
+ @VisibleForTesting
+ public SensorData() {
+ this(/* type= */ null, /* name= */ null);
+ }
+
+ @VisibleForTesting
+ public SensorData(String type, String name) {
+ this(type, name, /* minRefreshRate= */ 0f, /* maxRefreshRate= */ Float.POSITIVE_INFINITY);
+ }
+
+ @VisibleForTesting
+ public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate) {
+ this(type, name, minRefreshRate, maxRefreshRate, /* supportedModes= */ List.of());
+ }
+
+ @VisibleForTesting
+ public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate,
+ List<SupportedMode> supportedModes) {
+ this.type = type;
+ this.name = name;
+ this.minRefreshRate = minRefreshRate;
+ this.maxRefreshRate = maxRefreshRate;
+ this.supportedModes = Collections.unmodifiableList(supportedModes);
+ }
+
+ /**
+ * @return True if the sensor matches both the specified name and type, or one if only one
+ * is specified (not-empty). Always returns false if both parameters are null or empty.
+ */
+ public boolean matches(String sensorName, String sensorType) {
+ final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
+ final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
+ return (isNameSpecified || isTypeSpecified)
+ && (!isNameSpecified || sensorName.equals(name))
+ && (!isTypeSpecified || sensorType.equals(type));
+ }
+
+ @Override
+ public String toString() {
+ return "SensorData{"
+ + "type= " + type
+ + ", name= " + name
+ + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]"
+ + ", supportedModes=" + supportedModes
+ + '}';
+ }
+
+ /**
+ * Loads ambient light sensor data from DisplayConfiguration and if missing from resources xml
+ */
+ public static SensorData loadAmbientLightSensorConfig(DisplayConfiguration config,
+ Resources resources) {
+ SensorDetails sensorDetails = config.getLightSensor();
+ if (sensorDetails != null) {
+ return loadSensorData(sensorDetails);
+ } else {
+ return loadAmbientLightSensorConfig(resources);
+ }
+ }
+
+ /**
+ * Loads ambient light sensor data from resources xml
+ */
+ public static SensorData loadAmbientLightSensorConfig(Resources resources) {
+ return new SensorData(
+ resources.getString(com.android.internal.R.string.config_displayLightSensorType),
+ /* name= */ "");
+ }
+
+ /**
+ * Loads screen off brightness sensor data from DisplayConfiguration
+ */
+ public static SensorData loadScreenOffBrightnessSensorConfig(DisplayConfiguration config) {
+ SensorDetails sensorDetails = config.getScreenOffBrightnessSensor();
+ if (sensorDetails != null) {
+ return loadSensorData(sensorDetails);
+ } else {
+ return new SensorData();
+ }
+ }
+
+ /**
+ * Loads proximity sensor data from DisplayConfiguration
+ */
+ @Nullable
+ public static SensorData loadProxSensorConfig(DisplayConfiguration config) {
+ SensorDetails sensorDetails = config.getProxSensor();
+ if (sensorDetails != null) {
+ String name = sensorDetails.getName();
+ String type = sensorDetails.getType();
+ if ("".equals(name) && "".equals(type)) {
+ // <proxSensor> with empty values to the config means no sensor should be used.
+ // See also {@link com.android.server.display.utils.SensorUtils}
+ return null;
+ } else {
+ return loadSensorData(sensorDetails);
+ }
+ } else {
+ return new SensorData();
+ }
+ }
+
+ /**
+ * Loads sensor unspecified config, this means system should use default sensor.
+ * See also {@link com.android.server.display.utils.SensorUtils}
+ */
+ @NonNull
+ public static SensorData loadSensorUnspecifiedConfig() {
+ return new SensorData();
+ }
+
+ private static SensorData loadSensorData(@NonNull SensorDetails sensorDetails) {
+ float minRefreshRate = 0f;
+ float maxRefreshRate = Float.POSITIVE_INFINITY;
+ RefreshRateRange rr = sensorDetails.getRefreshRate();
+ if (rr != null) {
+ minRefreshRate = rr.getMinimum().floatValue();
+ maxRefreshRate = rr.getMaximum().floatValue();
+ }
+ ArrayList<SupportedMode> supportedModes = new ArrayList<>();
+ NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes();
+ if (configSupportedModes != null) {
+ for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) {
+ supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(),
+ supportedMode.getSecond().floatValue()));
+ }
+ }
+
+ return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate,
+ maxRefreshRate, supportedModes);
+ }
+
+ public static class SupportedMode {
+ public final float refreshRate;
+ public final float vsyncRate;
+
+ public SupportedMode(float refreshRate, float vsyncRate) {
+ this.refreshRate = refreshRate;
+ this.vsyncRate = vsyncRate;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d023913c9694..fb6c9e3cab9d 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -724,8 +724,9 @@ public class DisplayModeDirector {
|| mode.getPhysicalHeight() > maxAllowedHeight
|| mode.getPhysicalWidth() < outSummary.minWidth
|| mode.getPhysicalHeight() < outSummary.minHeight
- || mode.getRefreshRate() < outSummary.minPhysicalRefreshRate
- || mode.getRefreshRate() > outSummary.maxPhysicalRefreshRate) {
+ || mode.getRefreshRate() < (outSummary.minPhysicalRefreshRate - FLOAT_TOLERANCE)
+ || mode.getRefreshRate() > (outSummary.maxPhysicalRefreshRate + FLOAT_TOLERANCE)
+ ) {
continue;
}
diff --git a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
index 5cdef38cd45c..f57bf295a34e 100644
--- a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
+++ b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
@@ -17,6 +17,7 @@
package com.android.server.display.notifications;
import static android.app.Notification.COLOR_DEFAULT;
+
import static com.android.internal.notification.SystemNotificationChannels.ALERTS;
import android.annotation.Nullable;
@@ -112,7 +113,8 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete
sendErrorNotification(createErrorNotification(
R.string.connected_display_unavailable_notification_title,
- R.string.connected_display_unavailable_notification_content));
+ R.string.connected_display_unavailable_notification_content,
+ R.drawable.usb_cable_unknown_issue));
}
/**
@@ -129,7 +131,8 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete
sendErrorNotification(createErrorNotification(
R.string.connected_display_cable_dont_support_displays_notification_title,
- R.string.connected_display_cable_dont_support_displays_notification_content));
+ R.string.connected_display_cable_dont_support_displays_notification_content,
+ R.drawable.usb_cable_unknown_issue));
}
/**
@@ -144,7 +147,24 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete
sendErrorNotification(createErrorNotification(
R.string.connected_display_unavailable_notification_title,
- R.string.connected_display_unavailable_notification_content));
+ R.string.connected_display_unavailable_notification_content,
+ R.drawable.usb_cable_unknown_issue));
+ }
+
+ /**
+ * Send notification about high temperature preventing usage of the external display.
+ */
+ public void onHighTemperatureExternalDisplayNotAllowed() {
+ if (!mConnectedDisplayErrorHandlingEnabled) {
+ Slog.d(TAG, "onHighTemperatureExternalDisplayNotAllowed:"
+ + " mConnectedDisplayErrorHandlingEnabled is false");
+ return;
+ }
+
+ sendErrorNotification(createErrorNotification(
+ R.string.connected_display_unavailable_notification_title,
+ R.string.connected_display_thermally_unavailable_notification_content,
+ R.drawable.ic_thermostat_notification));
}
/**
@@ -176,7 +196,8 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete
/**
* @return a newly built notification about an issue with connected display.
*/
- private Notification createErrorNotification(final int titleId, final int messageId) {
+ private Notification createErrorNotification(final int titleId, final int messageId,
+ final int icon) {
final Resources resources = mContext.getResources();
final CharSequence title = resources.getText(titleId);
final CharSequence message = resources.getText(messageId);
@@ -190,7 +211,7 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete
return new Notification.Builder(mContext, ALERTS)
.setGroup(NOTIFICATION_GROUP_NAME)
- .setSmallIcon(R.drawable.usb_cable_unknown_issue)
+ .setSmallIcon(icon)
.setWhen(0)
.setTimeoutAfter(NOTIFICATION_TIMEOUT_MILLISEC)
.setOngoing(false)
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index 5d6e65071479..5f289349751f 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -61,12 +61,13 @@ public class DisplayStateController {
mPerformScreenOffTransition = true;
break;
case DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE:
- if (displayPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+ if (mDozeStateOverride != Display.STATE_UNKNOWN) {
+ state = mDozeStateOverride;
+ } else if (displayPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
state = displayPowerRequest.dozeScreenState;
} else {
state = Display.STATE_DOZE;
}
- state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
break;
case DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM:
case DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT:
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 56321cd01e20..8b9fe1083187 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -21,7 +21,7 @@ import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.text.TextUtils;
-import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.config.SensorData;
import java.util.List;
@@ -36,7 +36,7 @@ public class SensorUtils {
*/
@Nullable
public static Sensor findSensor(@Nullable SensorManager sensorManager,
- @Nullable DisplayDeviceConfig.SensorData sensorData, int fallbackType) {
+ @Nullable SensorData sensorData, int fallbackType) {
if (sensorData == null) {
return null;
} else {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index d3eedd7e089a..168715713f8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -191,9 +191,16 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
launchDeviceDiscovery();
startQueuedActions();
if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildRequestActiveSource(
- getDeviceInfo().getLogicalAddress()));
+ addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
+ getDeviceInfo().getLogicalAddress(),
+ getDeviceInfo().getPhysicalAddress()));
+ }
+ }
+ }));
}
}
@@ -1325,6 +1332,8 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
removeAction(TimerRecordingAction.class);
removeAction(NewDeviceAction.class);
removeAction(AbsoluteVolumeAudioStatusAction.class);
+ // Remove pending actions.
+ removeAction(RequestActiveSourceAction.class);
// Keep SAM enabled if eARC is enabled, unless we're going to Standby.
if (initiatedByCec || !mService.isEarcEnabled()){
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index c01bc2063a59..f992a2399c61 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -921,6 +921,9 @@ public class HdmiCecNetwork {
* port id.
*/
int portIdToPath(int portId) {
+ if (portId == Constants.CEC_SWITCH_HOME) {
+ return getPhysicalAddress();
+ }
HdmiPortInfo portInfo = getPortInfo(portId);
if (portInfo == null) {
Slog.e(TAG, "Cannot find the port info: " + portId);
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
new file mode 100644
index 000000000000..017c86d4b363
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
+
+/**
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ */
+public class RequestActiveSourceAction extends HdmiCecFeatureAction {
+ private static final String TAG = "RequestActiveSourceAction";
+
+ // State to wait for the <Active Source> message.
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1;
+
+ RequestActiveSourceAction(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ super(source, callback);
+ }
+
+ @Override
+ boolean start() {
+ Slog.v(TAG, "RequestActiveSourceAction started.");
+
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ // The action finishes successfully if the <Active Source> message is received.
+ // {@link HdmiCecLocalDevice#onMessage} handles this message, so false is returned.
+ if (cmd.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+ if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) {
+ finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index aab491ee1def..8e0289ef1b43 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -47,9 +47,6 @@ class InputSettingsObserver extends ContentObserver {
private final NativeInputManagerService mNative;
private final Map<Uri, Consumer<String /* reason*/>> mObservers;
- // Cache prevent notifying same KeyRepeatInfo data to native code multiple times.
- private KeyRepeatInfo mLastKeyRepeatInfoSettingsUpdate;
-
InputSettingsObserver(Context context, Handler handler, InputManagerService service,
NativeInputManagerService nativeIms) {
super(handler);
@@ -84,9 +81,9 @@ class InputSettingsObserver extends ContentObserver {
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_KEY_PRESSES),
(reason) -> updateShowKeyPresses()),
Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_TIMEOUT_MS),
- (reason) -> updateKeyRepeatInfo(getLatestLongPressTimeoutValue())),
+ (reason) -> updateKeyRepeatInfo()),
Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_DELAY_MS),
- (reason) -> updateKeyRepeatInfo(getLatestLongPressTimeoutValue())),
+ (reason) -> updateKeyRepeatInfo()),
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT),
(reason) -> updateShowRotaryInput()));
}
@@ -182,46 +179,32 @@ class InputSettingsObserver extends ContentObserver {
}
private void updateLongPressTimeout(String reason) {
- final int longPressTimeoutValue = getLatestLongPressTimeoutValue();
-
- // Before the key repeat timeout was introduced, some users relied on changing
- // LONG_PRESS_TIMEOUT settings to also change the key repeat timeout. To support this
- // backward compatibility, we'll preemptively update key repeat info here, in case where
- // key repeat timeout was never set, and user is still relying on long press timeout value.
- updateKeyRepeatInfo(longPressTimeoutValue);
+ // Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value.
+ final int longPressTimeoutMs = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT,
+ UserHandle.USER_CURRENT);
final boolean featureEnabledFlag =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DEEP_PRESS_ENABLED, true /* default */);
final boolean enabled =
featureEnabledFlag
- && longPressTimeoutValue <= ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
+ && longPressTimeoutMs <= ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
Log.i(TAG, (enabled ? "Enabling" : "Disabling") + " motion classifier because " + reason
+ ": feature " + (featureEnabledFlag ? "enabled" : "disabled")
- + ", long press timeout = " + longPressTimeoutValue);
+ + ", long press timeout = " + longPressTimeoutMs + " ms");
mNative.setMotionClassifierEnabled(enabled);
}
- private void updateKeyRepeatInfo(int fallbackKeyRepeatTimeoutValue) {
- // Not using ViewConfiguration.getKeyRepeatTimeout here because it may return a stale value.
+ private void updateKeyRepeatInfo() {
+ // Use ViewConfiguration getters only as fallbacks because they may return stale values.
final int timeoutMs = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.KEY_REPEAT_TIMEOUT_MS, fallbackKeyRepeatTimeoutValue,
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(),
UserHandle.USER_CURRENT);
final int delayMs = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
UserHandle.USER_CURRENT);
- if (mLastKeyRepeatInfoSettingsUpdate == null || !mLastKeyRepeatInfoSettingsUpdate.isEqualTo(
- timeoutMs, delayMs)) {
- mNative.setKeyRepeatConfiguration(timeoutMs, delayMs);
- mLastKeyRepeatInfoSettingsUpdate = new KeyRepeatInfo(timeoutMs, delayMs);
- }
- }
-
- // Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value.
- private int getLatestLongPressTimeoutValue() {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT,
- UserHandle.USER_CURRENT);
+ mNative.setKeyRepeatConfiguration(timeoutMs, delayMs);
}
private void updateMaximumObscuringOpacityForTouch() {
@@ -233,19 +216,4 @@ class InputSettingsObserver extends ContentObserver {
}
mNative.setMaximumObscuringOpacityForTouch(opacity);
}
-
- private static class KeyRepeatInfo {
- private final int mKeyRepeatTimeoutMs;
- private final int mKeyRepeatDelayMs;
-
- private KeyRepeatInfo(int keyRepeatTimeoutMs, int keyRepeatDelayMs) {
- this.mKeyRepeatTimeoutMs = keyRepeatTimeoutMs;
- this.mKeyRepeatDelayMs = keyRepeatDelayMs;
- }
-
- public boolean isEqualTo(int keyRepeatTimeoutMs, int keyRepeatDelayMs) {
- return mKeyRepeatTimeoutMs == keyRepeatTimeoutMs
- && mKeyRepeatDelayMs == keyRepeatDelayMs;
- }
- }
}
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
index 2d6b013d11c1..035a7485fe86 100644
--- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -39,7 +39,6 @@ final class AutofillSuggestionsController {
private static final String TAG = AutofillSuggestionsController.class.getSimpleName();
@NonNull private final InputMethodManagerService mService;
- @NonNull private final InputMethodUtils.InputMethodSettings mSettings;
private static final class CreateInlineSuggestionsRequest {
@NonNull final InlineSuggestionsRequestInfo mRequestInfo;
@@ -76,7 +75,6 @@ final class AutofillSuggestionsController {
AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
mService = service;
- mSettings = mService.mSettings;
}
@GuardedBy("ImfLock.class")
@@ -88,7 +86,7 @@ final class AutofillSuggestionsController {
final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked(
mService.getSelectedMethodIdLocked());
try {
- if (userId == mSettings.getCurrentUserId()
+ if (userId == mService.getCurrentImeUserIdLocked()
&& imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
requestInfo, callback, imi.getPackageName());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index e76aa1aad0cc..c8c0482f5a9d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -63,7 +63,6 @@ final class InputMethodBindingController {
@NonNull private final InputMethodManagerService mService;
@NonNull private final Context mContext;
- @NonNull private final InputMethodUtils.InputMethodSettings mSettings;
@NonNull private final PackageManagerInternal mPackageManagerInternal;
@NonNull private final WindowManagerInternal mWindowManagerInternal;
@@ -113,7 +112,6 @@ final class InputMethodBindingController {
int imeConnectionBindFlags, CountDownLatch latchForTesting) {
mService = service;
mContext = mService.mContext;
- mSettings = mService.mSettings;
mPackageManagerInternal = mService.mPackageManagerInternal;
mWindowManagerInternal = mService.mWindowManagerInternal;
mImeConnectionBindFlags = imeConnectionBindFlags;
@@ -322,7 +320,7 @@ final class InputMethodBindingController {
private void updateCurrentMethodUid() {
final String curMethodPackage = mCurIntent.getComponent().getPackageName();
final int curMethodUid = mPackageManagerInternal.getPackageUid(
- curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
+ curMethodPackage, 0 /* flags */, mService.getCurrentImeUserIdLocked());
if (curMethodUid < 0) {
Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage);
mCurMethodUid = Process.INVALID_UID;
@@ -478,7 +476,7 @@ final class InputMethodBindingController {
return false;
}
return mContext.bindServiceAsUser(mCurIntent, conn, flags,
- new UserHandle(mSettings.getCurrentUserId()));
+ new UserHandle(mService.getCurrentImeUserIdLocked()));
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 3e46ee29346e..14daf62a9ed2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -56,9 +56,13 @@ public abstract class InputMethodManagerInternal {
public abstract void setInteractive(boolean interactive);
/**
- * Hides the current input method, if visible.
+ * Hides the input methods for all the users, if visible.
+ *
+ * @param reason the reason for hiding the current input method
+ * @param originatingDisplayId the display ID the request is originated
*/
- public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason);
+ public abstract void hideAllInputMethods(@SoftInputShowHideReason int reason,
+ int originatingDisplayId);
/**
* Returns the list of installed input methods for the specified user.
@@ -171,17 +175,20 @@ public abstract class InputMethodManagerInternal {
*
* @param accessibilityConnectionId the connection id of the accessibility service
* @param session the session passed back from the accessibility service
+ * @param userId the user ID to be queried
*/
public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IAccessibilityInputMethodSession session);
+ IAccessibilityInputMethodSession session, @UserIdInt int userId);
/**
* Unbind the accessibility service with the specified accessibilityConnectionId from current
* client.
*
* @param accessibilityConnectionId the connection id of the accessibility service
+ * @param userId the user ID to be queried
*/
- public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
+ public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
+ @UserIdInt int userId);
/**
* Switch the keyboard layout in response to a keyboard shortcut.
@@ -207,7 +214,8 @@ public abstract class InputMethodManagerInternal {
}
@Override
- public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
+ public void hideAllInputMethods(@SoftInputShowHideReason int reason,
+ int originatingDisplayId) {
}
@Override
@@ -266,11 +274,12 @@ public abstract class InputMethodManagerInternal {
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IAccessibilityInputMethodSession session) {
+ IAccessibilityInputMethodSession session, @UserIdInt int userId) {
}
@Override
- public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
+ public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
+ @UserIdInt int userId) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1ae67dd8add9..ddb32fe09d6c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -20,6 +20,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
import static android.provider.Settings.Secure.STYLUS_HANDWRITING_DEFAULT_VALUE;
import static android.provider.Settings.Secure.STYLUS_HANDWRITING_ENABLED;
import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION;
@@ -225,7 +226,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
- private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
+ private static final int MSG_HIDE_ALL_INPUT_METHODS = 1035;
private static final int MSG_REMOVE_IME_SURFACE = 1060;
private static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
private static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
@@ -1727,6 +1728,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
registerDeviceListenerAndCheckStylusSupport();
}
+ @GuardedBy("ImfLock.class")
+ @UserIdInt
+ int getCurrentImeUserIdLocked() {
+ return mSettings.getCurrentUserId();
+ }
+
private final class InkWindowInitializer implements Runnable {
public void run() {
synchronized (ImfLock.class) {
@@ -4828,7 +4835,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// ---------------------------------------------------------
- case MSG_HIDE_CURRENT_INPUT_METHOD:
+ case MSG_HIDE_ALL_INPUT_METHODS:
synchronized (ImfLock.class) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
@@ -5584,9 +5591,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
- mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
- mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
+ public void hideAllInputMethods(@SoftInputShowHideReason int reason,
+ int originatingDisplayId) {
+ mHandler.removeMessages(MSG_HIDE_ALL_INPUT_METHODS);
+ mHandler.obtainMessage(MSG_HIDE_ALL_INPUT_METHODS, reason).sendToTarget();
}
@Override
@@ -5709,8 +5717,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IAccessibilityInputMethodSession session) {
+ IAccessibilityInputMethodSession session, @UserIdInt int userId) {
synchronized (ImfLock.class) {
+ // TODO(b/305829876): Implement user ID verification
if (mCurClient != null) {
clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
mCurClient.mAccessibilitySessions.put(accessibilityConnectionId,
@@ -5737,8 +5746,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
+ public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
+ @UserIdInt int userId) {
synchronized (ImfLock.class) {
+ // TODO(b/305829876): Implement user ID verification
if (mCurClient != null) {
if (DEBUG) {
Slog.v(TAG, "unbindAccessibilityFromCurrentClientLocked: client="
@@ -6459,6 +6470,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
}
+ // Skip on headless user
+ if (USER_TYPE_SYSTEM_HEADLESS.equals(
+ mUserManagerInternal.getUserInfo(userId).userType)) {
+ continue;
+ }
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getCurrentUserId()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 86e417b183f8..efa1e0d66f35 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -77,13 +77,15 @@ final class InputMethodMenuController {
void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
- final boolean isScreenLocked = isScreenLocked();
-
- final String lastInputMethodId = mSettings.getSelectedInputMethod();
- int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
- if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
-
synchronized (ImfLock.class) {
+ final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
+ && mWindowManagerInternal.isKeyguardSecure(
+ mService.getCurrentImeUserIdLocked());
+ final String lastInputMethodId = mSettings.getSelectedInputMethod();
+ int lastInputMethodSubtypeId =
+ mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+ if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
+
final List<ImeSubtypeListItem> imList = mSwitchingController
.getSortedInputMethodAndSubtypeListForImeMenuLocked(
showAuxSubtypes, isScreenLocked);
@@ -200,12 +202,8 @@ final class InputMethodMenuController {
mService.updateSystemUiLocked();
mService.sendOnNavButtonFlagsChangedLocked();
mSwitchingDialog.show();
- }
- }
- private boolean isScreenLocked() {
- return mWindowManagerInternal.isKeyguardLocked()
- && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId());
+ }
}
void updateKeyboardFromSettingsLocked() {
diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
index f49fa6ecca4a..0185190521a3 100644
--- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
@@ -86,8 +86,7 @@ final class SubtypeUtils {
continue;
}
}
- if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode)
- || mode.equalsIgnoreCase(subtype.getMode())) {
+ if (TextUtils.isEmpty(mode) || mode.equalsIgnoreCase(subtype.getMode())) {
return true;
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 568618e0a065..57e424d944f5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -243,6 +243,10 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String MIGRATED_FRP2 = "migrated_frp2";
private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
+ private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
+
+ private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
+ android.security.Flags.fixUnlockedDeviceRequiredKeys();
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
@@ -856,9 +860,11 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
- // Notify keystore that a new user was added.
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ // Notify keystore that a new user was added.
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ }
} else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
@@ -1022,24 +1028,53 @@ public class LockSettingsService extends ILockSettings.Stub {
}
mEarlyCreatedUsers = null; // no longer needed
- // Also do a one-time migration of all users to SP-based credentials with the CE key
- // encrypted by the SP. This is needed for the system user on the first boot of a
- // device, as the system user is special and never goes through the user creation flow
- // that other users do. It is also needed for existing users on a device upgraded from
- // Android 13 or earlier, where users with no LSKF didn't necessarily have an SP, and if
- // they did have an SP then their CE key wasn't encrypted by it.
+ // Do a one-time migration for any unsecured users: create the user's synthetic password
+ // if not already done, encrypt the user's CE key with the synthetic password if not
+ // already done, and create the user's Keystore super keys if not already done.
+ //
+ // This is needed for the following cases:
+ //
+ // - Finalizing the creation of the system user on the first boot of a device, as the
+ // system user is special and doesn't go through the normal user creation flow.
+ //
+ // - Upgrading from Android 13 or earlier, where unsecured users didn't necessarily have
+ // a synthetic password, and if they did have a synthetic password their CE key wasn't
+ // encrypted by it. Also, unsecured users didn't have Keystore super keys.
//
- // If this gets interrupted (e.g. by the device powering off), there shouldn't be a
- // problem since this will run again on the next boot, and setCeStorageProtection() is
- // okay with the CE key being already protected by the given secret.
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
- for (UserInfo user : mUserManager.getAliveUsers()) {
- removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
- synchronized (mSpManager) {
- migrateUserToSpWithBoundCeKeyLocked(user.id);
+ // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
+ //
+ // The end result is that all users, regardless of whether they are secured or not, have
+ // a synthetic password with all keys initialized and protected by it.
+ //
+ // Note: if this migration gets interrupted (e.g. by the device powering off), there
+ // shouldn't be a problem since this will run again on the next boot, and
+ // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundKeysLocked(user.id);
+ }
+ }
+ setBoolean(MIGRATED_SP_FULL, true, 0);
+ }
+ } else {
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundCeKeyLocked(user.id);
+ }
}
+ setString(MIGRATED_SP_CE_ONLY, "true", 0);
+ }
+
+ if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
+ // Ensure the full migration runs again the next time the flag is enabled...
+ setBoolean(MIGRATED_SP_FULL, false, 0);
}
- setString(MIGRATED_SP_CE_ONLY, "true", 0);
}
mThirdPartyAppsStarted = true;
@@ -1070,6 +1105,37 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ @GuardedBy("mSpManager")
+ private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
+ if (isUserSecure(userId)) {
+ Slogf.d(TAG, "User %d is secured; no migration needed", userId);
+ return;
+ }
+ long protectorId = getCurrentLskfBasedProtectorId(userId);
+ if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
+ Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
+ initializeSyntheticPassword(userId);
+ return;
+ }
+ Slogf.i(TAG, "Existing unsecured user %d has a synthetic password", userId);
+ AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
+ getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
+ null);
+ SyntheticPassword sp = result.syntheticPassword;
+ if (sp == null) {
+ Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
+ return;
+ }
+ // While setCeStorageProtection() is idempotent, it does log some error messages when called
+ // again. Skip it if we know it was already handled by an earlier upgrade to Android 14.
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
+ setCeStorageProtection(userId, sp);
+ }
+ Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
+ }
+
/**
* Returns the lowest password quality that still presents the same UI for entering it.
*
@@ -1351,6 +1417,20 @@ public class LockSettingsService extends ILockSettings.Stub {
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
+ final byte[] password = sp.deriveKeyStorePassword();
+ try {
+ int res = AndroidKeyStoreMaintenance.initUserSuperKeys(userId, password, allowExisting);
+ if (res != 0) {
+ throw new IllegalStateException("Failed to initialize Keystore super keys for user "
+ + userId);
+ }
+ } finally {
+ Arrays.fill(password, (byte) 0);
+ }
+ }
+
private void unlockKeystore(int userId, SyntheticPassword sp) {
Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
}
@@ -2074,6 +2154,9 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ unlockKeystore(userId, result.syntheticPassword);
+ }
unlockCeStorage(userId, result.syntheticPassword);
}
}
@@ -2353,6 +2436,16 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void createNewUser(@UserIdInt int userId, int userSerialNumber) {
+
+ // Delete all Keystore keys for userId, just in case any were left around from a removed
+ // user with the same userId. This should be unnecessary, but we've been doing this for a
+ // long time, so for now we keep doing it just in case it's ever important. Don't wait
+ // until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
+ // created during early boot, and maybe something will use Keystore before then.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserAdded(userId);
+ }
+
synchronized (mUserCreationAndRemovalLock) {
// During early boot, don't actually create the synthetic password yet, but rather
// automatically delay it to later. We do this because protecting the synthetic
@@ -2759,7 +2852,7 @@ public class LockSettingsService extends ILockSettings.Stub {
/**
* Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and
- * protects the user's CE key with a key derived from the SP.
+ * protects the user's CE storage key and Keystore super keys with keys derived from the SP.
*
* <p>This is called just once in the lifetime of the user: at user creation time (possibly
* delayed until the time when Weaver is guaranteed to be available), or when upgrading from
@@ -2778,6 +2871,9 @@ public class LockSettingsService extends ILockSettings.Stub {
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
setCeStorageProtection(userId, sp);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
+ }
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -2870,11 +2966,10 @@ public class LockSettingsService extends ILockSettings.Stub {
/**
* Changes the user's LSKF by creating an LSKF-based protector that uses the new LSKF (which may
* be empty) and replacing the old LSKF-based protector with it. The SP itself is not changed.
- *
- * Also maintains the invariants described in {@link SyntheticPasswordManager} by
- * setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the
- * LSKF is added/removed, respectively. If an LSKF is being added, then the Gatekeeper auth
- * token is also refreshed.
+ * <p>
+ * Also maintains the invariants described in {@link SyntheticPasswordManager} by enrolling /
+ * deleting the synthetic password into Gatekeeper as the LSKF is set / cleared, and asking
+ * Keystore to delete the user's auth-bound keys when the LSKF is cleared.
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
@@ -2893,7 +2988,9 @@ public class LockSettingsService extends ILockSettings.Stub {
if (!mSpManager.hasSidForUser(userId)) {
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
- setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ }
}
} else {
// Cache all profile password if they use unified work challenge. This will later be
@@ -2904,7 +3001,11 @@ public class LockSettingsService extends ILockSettings.Stub {
gateKeeperClearSecureUserId(userId);
unlockCeStorage(userId, sp);
unlockKeystore(userId, sp);
- setKeystorePassword(null, userId);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
+ } else {
+ setKeystorePassword(null, userId);
+ }
removeBiometricsForUser(userId);
}
setCurrentLskfBasedProtectorId(newProtectorId, userId);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 8e9c21f5f35f..cc205d4a53bd 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -90,10 +90,15 @@ import java.util.Set;
*
* - The user's credential-encrypted storage is always protected by the SP.
*
- * - The user's auth-bound Keystore keys are protected by the SP, but only while an LSKF is set.
- * This works by setting the user's Keystore and Gatekeeper passwords to SP-derived secrets, but
- * only while an LSKF is set. When the LSKF is removed, these passwords are cleared,
- * invalidating the user's auth-bound keys.
+ * - The user's Keystore superencryption keys are always protected by the SP. These in turn
+ * protect the Keystore keys that require user authentication, an unlocked device, or both.
+ *
+ * - A secret derived from the synthetic password is enrolled in Gatekeeper for the user, but only
+ * while the user has a (nonempty) LSKF. This enrollment has an associated ID called the Secure
+ * user ID or SID. This use of Gatekeeper, which is separate from the use of GateKeeper that may
+ * be used in the LSKF-based protector, makes it so that unlocking the synthetic password
+ * generates a HardwareAuthToken (but only when the user has LSKF). That HardwareAuthToken can
+ * be provided to KeyMint to authorize the use of the user's authentication-bound Keystore keys.
*
* Files stored on disk for each user:
* For the SP itself, stored under NULL_PROTECTOR_ID:
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 360a6a721988..6bdfae2dc02f 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -110,7 +110,7 @@ import java.util.Objects;
@Override
@NonNull
- public synchronized MediaRoute2Info getDeviceRoute() {
+ public synchronized MediaRoute2Info getSelectedRoute() {
if (mSelectedRoute != null) {
return mSelectedRoute;
}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 7876095a548a..0fdaaa7604e5 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -72,13 +72,9 @@ import com.android.media.flags.Flags;
*/
boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
- /**
- * Returns currently selected device (built-in or wired) route.
- *
- * @return non-null device route.
- */
+ /** Returns the currently selected device (built-in or wired) route. */
@NonNull
- MediaRoute2Info getDeviceRoute();
+ MediaRoute2Info getSelectedRoute();
/**
* Updates device route volume.
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 6ba40ae33f3c..65874e23dcdc 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -107,7 +107,7 @@ import java.util.Objects;
@Override
@NonNull
- public synchronized MediaRoute2Info getDeviceRoute() {
+ public synchronized MediaRoute2Info getSelectedRoute() {
return mDeviceRoute;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a158b18d91b4..994d3ca1124f 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -76,6 +76,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.view.KeyEvent;
import com.android.server.LocalServices;
@@ -348,16 +349,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
} else {
if (mVolumeControlType == VOLUME_CONTROL_FIXED) {
if (DEBUG) {
- Log.d(TAG, "Session does not support volume adjustment");
+ Slog.d(TAG, "Session does not support volume adjustment");
}
} else if (direction == AudioManager.ADJUST_TOGGLE_MUTE
|| direction == AudioManager.ADJUST_MUTE
|| direction == AudioManager.ADJUST_UNMUTE) {
- Log.w(TAG, "Muting remote playback is not supported");
+ Slog.w(TAG, "Muting remote playback is not supported");
} else {
if (DEBUG) {
- Log.w(TAG, "adjusting volume, pkg=" + packageName + ", asSystemService="
- + asSystemService + ", dir=" + direction);
+ Slog.w(
+ TAG,
+ "adjusting volume, pkg=" + packageName
+ + ", asSystemService=" + asSystemService
+ + ", dir=" + direction);
}
mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction);
@@ -371,8 +375,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
if (DEBUG) {
- Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is "
- + mMaxVolume);
+ Slog.d(
+ TAG,
+ "Adjusted optimistic volume to " + mOptimisticVolume
+ + " max is " + mMaxVolume);
}
}
// Always notify, even if the volume hasn't changed. This is important to ensure that
@@ -388,23 +394,33 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (mVolumeType == PLAYBACK_TYPE_LOCAL) {
int stream = getVolumeStream(mAudioAttrs);
final int volumeValue = value;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags,
- opPackageName, uid, pid,
- mContext.getApplicationInfo().targetSdkVersion);
- } catch (IllegalArgumentException | SecurityException e) {
- Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
- + ", flags=" + flags, e);
- }
- }
- });
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mAudioManager.setStreamVolumeForUid(
+ stream,
+ volumeValue,
+ flags,
+ opPackageName,
+ uid,
+ pid,
+ mContext.getApplicationInfo().targetSdkVersion);
+ } catch (IllegalArgumentException | SecurityException e) {
+ Slog.e(
+ TAG,
+ "Cannot set volume: stream=" + stream
+ + ", value=" + volumeValue
+ + ", flags=" + flags,
+ e);
+ }
+ }
+ });
} else {
if (mVolumeControlType != VOLUME_CONTROL_ABSOLUTE) {
if (DEBUG) {
- Log.d(TAG, "Session does not support setting volume");
+ Slog.d(TAG, "Session does not support setting volume");
}
} else {
value = Math.max(0, Math.min(value, mMaxVolume));
@@ -419,8 +435,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
if (DEBUG) {
- Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is "
- + mMaxVolume);
+ Slog.d(
+ TAG,
+ "Set optimistic volume to " + mOptimisticVolume
+ + " max is " + mMaxVolume);
}
}
// Always notify, even if the volume hasn't changed.
@@ -527,12 +545,27 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public boolean canHandleVolumeKey() {
if (isPlaybackTypeLocal()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Local MediaSessionRecord can handle volume key");
+ }
return true;
}
if (mVolumeControlType == VOLUME_CONTROL_FIXED) {
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ "Local MediaSessionRecord with FIXED volume control can't handle volume"
+ + " key");
+ }
return false;
}
if (mVolumeAdjustmentForRemoteGroupSessions) {
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ "Volume adjustment for remote group sessions allowed so MediaSessionRecord"
+ + " can handle volume key");
+ }
return true;
}
// See b/228021646 for details.
@@ -540,7 +573,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
List<RoutingSessionInfo> sessions = mRouter2Manager.getRoutingSessions(mPackageName);
boolean foundNonSystemSession = false;
boolean remoteSessionAllowVolumeAdjustment = true;
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ "Found "
+ + sessions.size()
+ + " routing sessions for package name "
+ + mPackageName);
+ }
for (RoutingSessionInfo session : sessions) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found routingSessionInfo: " + session);
+ }
if (!session.isSystemSession()) {
foundNonSystemSession = true;
if (session.getVolumeHandling() == PLAYBACK_VOLUME_FIXED) {
@@ -549,9 +593,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
}
if (!foundNonSystemSession) {
- Log.d(TAG, "Package " + mPackageName
- + " has a remote media session but no associated routing session");
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ "Package " + mPackageName
+ + " has a remote media session but no associated routing session");
+ }
}
+
return foundNonSystemSession && remoteSessionAllowVolumeAdjustment;
}
@@ -637,8 +686,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
final boolean asSystemService, final boolean useSuggested,
final int previousFlagPlaySound) {
if (DEBUG) {
- Log.w(TAG, "adjusting local volume, stream=" + stream + ", dir=" + direction
- + ", asSystemService=" + asSystemService + ", useSuggested=" + useSuggested);
+ Slog.w(
+ TAG,
+ "adjusting local volume, stream=" + stream + ", dir=" + direction
+ + ", asSystemService=" + asSystemService
+ + ", useSuggested=" + useSuggested);
}
// Must use opPackageName for adjusting volumes with UID.
final String opPackageName;
@@ -653,40 +705,61 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
uid = callingUid;
pid = callingPid;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- if (useSuggested) {
- if (AudioSystem.isStreamActive(stream, 0)) {
- mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
- direction, flags, opPackageName, uid, pid,
- mContext.getApplicationInfo().targetSdkVersion);
- } else {
- mAudioManager.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, opPackageName, uid, pid,
- mContext.getApplicationInfo().targetSdkVersion);
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (useSuggested) {
+ if (AudioSystem.isStreamActive(stream, 0)) {
+ mAudioManager.adjustSuggestedStreamVolumeForUid(
+ stream,
+ direction,
+ flags,
+ opPackageName,
+ uid,
+ pid,
+ mContext.getApplicationInfo().targetSdkVersion);
+ } else {
+ mAudioManager.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE,
+ direction,
+ flags | previousFlagPlaySound,
+ opPackageName,
+ uid,
+ pid,
+ mContext.getApplicationInfo().targetSdkVersion);
+ }
+ } else {
+ mAudioManager.adjustStreamVolumeForUid(
+ stream,
+ direction,
+ flags,
+ opPackageName,
+ uid,
+ pid,
+ mContext.getApplicationInfo().targetSdkVersion);
+ }
+ } catch (IllegalArgumentException | SecurityException e) {
+ Slog.e(
+ TAG,
+ "Cannot adjust volume: direction=" + direction
+ + ", stream=" + stream + ", flags=" + flags
+ + ", opPackageName=" + opPackageName + ", uid=" + uid
+ + ", useSuggested=" + useSuggested
+ + ", previousFlagPlaySound=" + previousFlagPlaySound,
+ e);
}
- } else {
- mAudioManager.adjustStreamVolumeForUid(stream, direction, flags,
- opPackageName, uid, pid,
- mContext.getApplicationInfo().targetSdkVersion);
}
- } catch (IllegalArgumentException | SecurityException e) {
- Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
- + stream + ", flags=" + flags + ", opPackageName=" + opPackageName
- + ", uid=" + uid + ", useSuggested=" + useSuggested
- + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
- }
- }
- });
+ });
}
private void logCallbackException(
String msg, ISessionControllerCallbackHolder holder, Exception e) {
- Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
- + ", exception=" + e);
+ Slog.v(
+ TAG,
+ msg + ", this=" + this + ", callback package=" + holder.mPackageName
+ + ", exception=" + e);
}
private void pushPlaybackStateUpdate() {
@@ -1083,7 +1156,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
throw new IllegalArgumentException(
"The media button receiver cannot be set to an activity.");
} else {
- Log.w(TAG, "Ignoring invalid media button receiver targeting an activity.");
+ Slog.w(
+ TAG,
+ "Ignoring invalid media button receiver targeting an activity.");
return;
}
}
@@ -1119,7 +1194,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) {
throw new IllegalArgumentException("Invalid component name: " + receiver);
} else {
- Log.w(
+ Slog.w(
TAG,
"setMediaButtonBroadcastReceiver(): "
+ "Ignoring invalid component name="
@@ -1258,7 +1333,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (attributes != null) {
mAudioAttrs = attributes;
} else {
- Log.e(TAG, "Received null audio attributes, using existing attributes");
+ Slog.e(TAG, "Received null audio attributes, using existing attributes");
}
}
if (typeChanged) {
@@ -1320,7 +1395,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
return true;
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1343,7 +1418,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
return true;
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1356,7 +1431,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onCommand(packageName, pid, uid, command, args, cb);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in sendCommand.", e);
+ Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
@@ -1368,7 +1443,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onCustomAction(packageName, pid, uid, action, args);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in sendCustomAction.", e);
+ Slog.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
@@ -1379,7 +1454,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPrepare(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in prepare.", e);
+ Slog.e(TAG, "Remote failure in prepare.", e);
}
}
@@ -1391,7 +1466,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
+ Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
@@ -1403,7 +1478,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in prepareFromSearch.", e);
+ Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
@@ -1414,7 +1489,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in prepareFromUri.", e);
+ Slog.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
@@ -1425,7 +1500,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPlay(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in play.", e);
+ Slog.e(TAG, "Remote failure in play.", e);
}
}
@@ -1437,7 +1512,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in playFromMediaId.", e);
+ Slog.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
@@ -1449,7 +1524,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in playFromSearch.", e);
+ Slog.e(TAG, "Remote failure in playFromSearch.", e);
}
}
@@ -1460,7 +1535,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in playFromUri.", e);
+ Slog.e(TAG, "Remote failure in playFromUri.", e);
}
}
@@ -1471,7 +1546,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onSkipToTrack(packageName, pid, uid, id);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in skipToTrack", e);
+ Slog.e(TAG, "Remote failure in skipToTrack", e);
}
}
@@ -1482,7 +1557,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPause(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in pause.", e);
+ Slog.e(TAG, "Remote failure in pause.", e);
}
}
@@ -1493,7 +1568,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onStop(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in stop.", e);
+ Slog.e(TAG, "Remote failure in stop.", e);
}
}
@@ -1504,7 +1579,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onNext(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in next.", e);
+ Slog.e(TAG, "Remote failure in next.", e);
}
}
@@ -1515,7 +1590,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onPrevious(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in previous.", e);
+ Slog.e(TAG, "Remote failure in previous.", e);
}
}
@@ -1526,7 +1601,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onFastForward(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in fastForward.", e);
+ Slog.e(TAG, "Remote failure in fastForward.", e);
}
}
@@ -1537,7 +1612,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onRewind(packageName, pid, uid);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in rewind.", e);
+ Slog.e(TAG, "Remote failure in rewind.", e);
}
}
@@ -1548,7 +1623,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onSeekTo(packageName, pid, uid, pos);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in seekTo.", e);
+ Slog.e(TAG, "Remote failure in seekTo.", e);
}
}
@@ -1559,7 +1634,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onRate(packageName, pid, uid, rating);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in rate.", e);
+ Slog.e(TAG, "Remote failure in rate.", e);
}
}
@@ -1570,7 +1645,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+ Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
}
}
@@ -1587,7 +1662,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mCb.onAdjustVolume(packageName, pid, uid, direction);
}
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in adjustVolume.", e);
+ Slog.e(TAG, "Remote failure in adjustVolume.", e);
}
}
@@ -1598,7 +1673,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
pid, uid, packageName, reason);
mCb.onSetVolumeTo(packageName, pid, uid, value);
} catch (RemoteException e) {
- Log.e(TAG, "Remote failure in setVolumeTo.", e);
+ Slog.e(TAG, "Remote failure in setVolumeTo.", e);
}
}
@@ -1641,8 +1716,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb));
mControllerCallbackHolders.add(holder);
if (DEBUG) {
- Log.d(TAG, "registering controller callback " + cb + " from controller"
- + packageName);
+ Slog.d(
+ TAG,
+ "registering controller callback " + cb
+ + " from controller" + packageName);
}
// Avoid callback leaks
try {
@@ -1651,7 +1728,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
cb.asBinder().linkToDeath(holder.mDeathMonitor, 0);
} catch (RemoteException e) {
unregisterCallback(cb);
- Log.w(TAG, "registerCallback failed to linkToDeath", e);
+ Slog.w(TAG, "registerCallback failed to linkToDeath", e);
}
}
}
@@ -1666,12 +1743,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
cb.asBinder().unlinkToDeath(
mControllerCallbackHolders.get(index).mDeathMonitor, 0);
} catch (NoSuchElementException e) {
- Log.w(TAG, "error unlinking to binder death", e);
+ Slog.w(TAG, "error unlinking to binder death", e);
}
mControllerCallbackHolders.remove(index);
}
if (DEBUG) {
- Log.d(TAG, "unregistering callback " + cb.asBinder());
+ Slog.d(TAG, "unregistering callback " + cb.asBinder());
}
}
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 78077a831622..c8dba800a017 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -228,8 +228,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
return;
}
- MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
- if (TextUtils.equals(routeId, deviceRoute.getId())) {
+ MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
+ if (TextUtils.equals(routeId, selectedDeviceRoute.getId())) {
mBluetoothRouteController.transferTo(null);
} else {
mBluetoothRouteController.transferTo(routeId);
@@ -278,11 +278,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
return null;
}
- MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
+ MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
SYSTEM_SESSION_ID, packageName).setSystemSession(true);
- builder.addSelectedRoute(deviceRoute.getId());
+ builder.addSelectedRoute(selectedDeviceRoute.getId());
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addTransferableRoute(route.getId());
}
@@ -314,7 +314,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
// We must have a device route in the provider info.
- builder.addRoute(mDeviceRouteController.getDeviceRoute());
+ builder.addRoute(mDeviceRouteController.getSelectedRoute());
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addRoute(route);
@@ -338,12 +338,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
SYSTEM_SESSION_ID, "" /* clientPackageName */)
.setSystemSession(true);
- MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
- MediaRoute2Info selectedRoute = deviceRoute;
+ MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
+ MediaRoute2Info selectedRoute = selectedDeviceRoute;
MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute();
if (selectedBtRoute != null) {
selectedRoute = selectedBtRoute;
- builder.addTransferableRoute(deviceRoute.getId());
+ builder.addTransferableRoute(selectedDeviceRoute.getId());
}
mSelectedRouteId = selectedRoute.getId();
mDefaultRoute =
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 1b7d1ba59b06..9a0b3914122c 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -208,7 +208,7 @@ public class LockdownVpnTracker {
// network is the system default. So, if the VPN is up and underlying network
// (e.g., wifi) disconnects, CS will inform apps that the VPN's capabilities have
// changed to match the new default network (e.g., cell).
- mVpn.startLegacyVpnPrivileged(mProfile, network, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 550ad5d610da..681d1a0ee10a 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -74,7 +74,6 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.flags.Flags;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.server.FgThread;
@@ -328,10 +327,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
/**
* Notify our observers of a change in the data activity state of the interface
*/
- private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
+ private void notifyInterfaceClassActivity(int label, boolean isActive, long tsNanos,
int uid) {
invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- type, isActive, tsNanos, uid));
+ label, isActive, tsNanos, uid));
}
// Sync the state of the given chain with the native daemon.
@@ -1062,7 +1061,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDataSaverModeEnabled");
try {
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
// setDataSaverEnabled throws if it fails to set data saver.
mContext.getSystemService(ConnectivityManager.class)
.setDataSaverEnabled(enable);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1bdd402cf0b5..bae06347d8a2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -179,6 +179,8 @@ import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
+import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -358,6 +360,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -546,6 +549,15 @@ public class NotificationManagerService extends SystemService {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L;
+ /**
+ * App calls to {@link android.app.NotificationManager#setInterruptionFilter} and
+ * {@link android.app.NotificationManager#setNotificationPolicy} manage DND through the
+ * creation and activation of an implicit {@link android.app.AutomaticZenRule}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES = 308670109L;
+
private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
private IActivityManager mAm;
@@ -5234,13 +5246,23 @@ public class NotificationManagerService extends SystemService {
}
}
+ // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
@Override
public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
- enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+ enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
return mZenModeHelper.getZenRules();
}
@Override
+ public Map<String, AutomaticZenRule> getAutomaticZenRules() {
+ if (!android.app.Flags.modesApi()) {
+ throw new IllegalStateException("getAutomaticZenRules called with flag off!");
+ }
+ enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+ return mZenModeHelper.getAutomaticZenRules();
+ }
+
+ @Override
public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
@@ -5343,6 +5365,12 @@ public class NotificationManagerService extends SystemService {
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+ if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
+ return;
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
@@ -5426,6 +5454,16 @@ public class NotificationManagerService extends SystemService {
}
}
+ private boolean canManageGlobalZenPolicy(String callingPkg, int callingUid) {
+ boolean isCompatChangeEnabled = Binder.withCleanCallingIdentity(
+ () -> CompatChanges.isChangeEnabled(MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES,
+ callingUid));
+ return !isCompatChangeEnabled
+ || isCallerIsSystemOrSystemUi()
+ || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
+ AssociationRequest.DEVICE_PROFILE_WATCH);
+ }
+
private void enforcePolicyAccess(String pkg, String method) {
if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
@@ -5619,6 +5657,10 @@ public class NotificationManagerService extends SystemService {
@Override
public Policy getNotificationPolicy(String pkg) {
+ final int callingUid = Binder.getCallingUid();
+ if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(pkg);
+ }
final long identity = Binder.clearCallingIdentity();
try {
return mZenModeHelper.getNotificationPolicy();
@@ -5649,6 +5691,10 @@ public class NotificationManagerService extends SystemService {
enforcePolicyAccess(pkg, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+ boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
+ && !canManageGlobalZenPolicy(pkg, callingUid);
+
final long identity = Binder.clearCallingIdentity();
try {
final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
@@ -5687,16 +5733,21 @@ public class NotificationManagerService extends SystemService {
policy = new Policy(policy.priorityCategories,
policy.priorityCallSenders, policy.priorityMessageSenders,
newVisualEffects, policy.priorityConversationSenders);
- ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
- mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+
+ if (shouldApplyAsImplicitRule) {
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+ } else {
+ ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
+ policy);
+ mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+ }
} catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set notification policy", e);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
-
-
@Override
public List<String> getEnabledNotificationListenerPackages() {
checkCallerIsSystem();
@@ -6991,9 +7042,8 @@ public class NotificationManagerService extends SystemService {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
String shortcutId = n.getShortcutId();
- final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
- pkg, notificationUid, channelId, shortcutId,
- true /* parent ok */, false /* includeDeleted */);
+ final NotificationChannel channel = getNotificationChannelRestoreDeleted(pkg,
+ callingUid, notificationUid, channelId, shortcutId);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
@@ -7111,6 +7161,35 @@ public class NotificationManagerService extends SystemService {
return true;
}
+ /**
+ * Returns a channel, if exists, and restores deleted conversation channels.
+ */
+ @Nullable
+ private NotificationChannel getNotificationChannelRestoreDeleted(String pkg,
+ int callingUid, int notificationUid, String channelId, String conversationId) {
+ // Restore a deleted conversation channel, if exists. Otherwise use the parent channel.
+ NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
+ pkg, notificationUid, channelId, conversationId,
+ true /* parent ok */, !TextUtils.isEmpty(conversationId) /* includeDeleted */);
+ // Restore deleted conversation channel
+ if (channel != null && channel.isDeleted()) {
+ if (Objects.equals(conversationId, channel.getConversationId())) {
+ boolean needsPolicyFileChange = mPreferencesHelper.createNotificationChannel(
+ pkg, notificationUid, channel, true /* fromTargetApp */,
+ mConditionProviders.isPackageOrComponentAllowed(pkg,
+ UserHandle.getUserId(notificationUid)), callingUid, true);
+ // Update policy file if the conversation channel was restored
+ if (needsPolicyFileChange) {
+ handleSavePolicyFile();
+ }
+ } else {
+ // Do not restore parent channel
+ channel = null;
+ }
+ }
+ return channel;
+ }
+
private void onConversationRemovedInternal(String pkg, int uid, Set<String> shortcuts) {
checkCallerIsSystem();
Preconditions.checkStringNotEmpty(pkg);
@@ -10556,6 +10635,12 @@ public class NotificationManagerService extends SystemService {
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
+ return hasCompanionDevice(info.component.getPackageName(),
+ info.userid, /* withDeviceProfile= */ null);
+ }
+
+ private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
+ @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
if (mCompanionManager == null) {
mCompanionManager = getCompanionManager();
}
@@ -10565,17 +10650,19 @@ public class NotificationManagerService extends SystemService {
}
final long identity = Binder.clearCallingIdentity();
try {
- List<?> associations = mCompanionManager.getAssociations(
- info.component.getPackageName(), info.userid);
- if (!ArrayUtils.isEmpty(associations)) {
- return true;
+ List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
+ for (AssociationInfo association : associations) {
+ if (withDeviceProfile == null || withDeviceProfile.equals(
+ association.getDeviceProfile())) {
+ return true;
+ }
}
} catch (SecurityException se) {
// Not a privileged listener
} catch (RemoteException re) {
Slog.e(TAG, "Cannot reach companion device service", re);
} catch (Exception e) {
- Slog.e(TAG, "Cannot verify listener " + info, e);
+ Slog.e(TAG, "Cannot verify caller pkg=" + pkg + ", userId=" + userId, e);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -11401,17 +11488,16 @@ public class NotificationManagerService extends SystemService {
}
}
}
- }
- // clean up anything in the disallowed pkgs list
- for (int i = 0; i < pkgList.length; i++) {
- String pkg = pkgList[i];
- for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
- NotificationListenerFilter nlf =
- mRequestedNotificationListeners.valueAt(j);
-
- VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
- nlf.removePackage(ai);
+ // Clean up removed package from the disallowed packages list
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ NotificationListenerFilter nlf =
+ mRequestedNotificationListeners.valueAt(j);
+ VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+ nlf.removePackage(ai);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 8f5676b31594..9106c33d26db 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -118,7 +118,10 @@ public final class SnoozeHelper {
protected boolean canSnooze(int numberToSnooze) {
synchronized (mLock) {
- if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
+ if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT
+ || (mPersistedSnoozedNotifications.size()
+ + mPersistedSnoozedNotificationsWithContext.size() + numberToSnooze)
+ > CONCURRENT_SNOOZE_LIMIT) {
return false;
}
}
@@ -357,6 +360,9 @@ public final class SnoozeHelper {
if (groupSummaryKey != null) {
NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey);
+ String trimmedKey = getTrimmedString(groupSummaryKey);
+ mPersistedSnoozedNotificationsWithContext.remove(trimmedKey);
+ mPersistedSnoozedNotifications.remove(trimmedKey);
if (record != null && !record.isCanceled) {
Runnable runnable = () -> {
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/services/core/java/com/android/server/notification/ZenAdapters.java
new file mode 100644
index 000000000000..2a65aff7f28d
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenAdapters.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+/**
+ * Converters between different Zen representations.
+ */
+class ZenAdapters {
+
+ static ZenPolicy notificationPolicyToZenPolicy(Policy policy) {
+ ZenPolicy.Builder zenPolicyBuilder = new ZenPolicy.Builder()
+ .allowAlarms(policy.allowAlarms())
+ .allowCalls(
+ policy.allowCalls()
+ ? ZenModeConfig.getZenPolicySenders(policy.allowCallsFrom())
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowConversations(
+ policy.allowConversations()
+ ? notificationPolicyConversationSendersToZenPolicy(
+ policy.allowConversationsFrom())
+ : ZenPolicy.CONVERSATION_SENDERS_NONE)
+ .allowEvents(policy.allowEvents())
+ .allowMedia(policy.allowMedia())
+ .allowMessages(
+ policy.allowMessages()
+ ? ZenModeConfig.getZenPolicySenders(policy.allowMessagesFrom())
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowReminders(policy.allowReminders())
+ .allowRepeatCallers(policy.allowRepeatCallers())
+ .allowSystem(policy.allowSystem());
+
+ if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+ zenPolicyBuilder.showBadges(policy.showBadges())
+ .showFullScreenIntent(policy.showFullScreenIntents())
+ .showInAmbientDisplay(policy.showAmbient())
+ .showInNotificationList(policy.showInNotificationList())
+ .showLights(policy.showLights())
+ .showPeeking(policy.showPeeking())
+ .showStatusBarIcons(policy.showStatusBarIcons());
+ }
+
+ return zenPolicyBuilder.build();
+ }
+
+ @ZenPolicy.ConversationSenders
+ private static int notificationPolicyConversationSendersToZenPolicy(
+ int npPriorityConversationSenders) {
+ switch (npPriorityConversationSenders) {
+ case Policy.CONVERSATION_SENDERS_ANYONE:
+ return ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+ case Policy.CONVERSATION_SENDERS_IMPORTANT:
+ return ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+ case Policy.CONVERSATION_SENDERS_NONE:
+ return ZenPolicy.CONVERSATION_SENDERS_NONE;
+ case Policy.CONVERSATION_SENDERS_UNSET:
+ default:
+ return ZenPolicy.CONVERSATION_SENDERS_UNSET;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9802adf302d1..cb05084ea3f0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -16,14 +16,20 @@
package com.android.server.notification;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
import android.app.Flags;
@@ -31,11 +37,15 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -51,6 +61,7 @@ import android.media.AudioSystem;
import android.media.VolumePolicy;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -95,7 +106,9 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -109,6 +122,13 @@ public class ZenModeHelper {
private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
static final int RULE_LIMIT_PER_PACKAGE = 100;
+ /**
+ * Send new activation AutomaticZenRule statuses to apps with a min target SDK version
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L;
+
// pkg|userId => uid
@VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
@@ -229,7 +249,8 @@ public class ZenModeHelper {
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
updateConfigAndZenModeLocked(mConfig, "init", true /*setRingerMode*/,
- Process.SYSTEM_UID /* callingUid */, true /* is system */);
+ Process.SYSTEM_UID /* callingUid */, true /* is system */,
+ false /* no broadcasts*/);
}
}
@@ -315,6 +336,7 @@ public class ZenModeHelper {
return mZenMode;
}
+ // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
synchronized (mConfigLock) {
@@ -328,15 +350,29 @@ public class ZenModeHelper {
return rules;
}
+ /**
+ * Get the list of {@link AutomaticZenRule} instances that the calling package can manage
+ * (which means the owned rules for a regular app, and every rule for system callers) together
+ * with their ids.
+ */
+ Map<String, AutomaticZenRule> getAutomaticZenRules() {
+ List<ZenRule> ruleList = getZenRules();
+ HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
+ for (ZenRule rule : ruleList) {
+ rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
+ }
+ return rules;
+ }
+
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
synchronized (mConfigLock) {
if (mConfig == null) return null;
- rule = mConfig.automaticRules.get(id);
+ rule = mConfig.automaticRules.get(id);
}
if (rule == null) return null;
if (canManageAutomaticZenRule(rule)) {
- return createAutomaticZenRule(rule);
+ return zenRuleToAutomaticZenRule(rule);
}
return null;
}
@@ -407,10 +443,13 @@ public class ZenModeHelper {
"Cannot update rules not owned by your condition provider");
}
}
- if (rule.enabled != automaticZenRule.isEnabled()) {
- dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.getPkg(), ruleId,
- automaticZenRule.isEnabled()
- ? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED);
+ if (!Flags.modesApi()) {
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.getPkg(), ruleId,
+ automaticZenRule.isEnabled()
+ ? AUTOMATIC_RULE_STATUS_ENABLED
+ : AUTOMATIC_RULE_STATUS_DISABLED);
+ }
}
populateZenRule(rule.pkg, automaticZenRule, rule, false);
@@ -419,6 +458,167 @@ public class ZenModeHelper {
}
}
+ /**
+ * Create (or activate, or deactivate) an "implicit" {@link ZenRule} when an app that has
+ * Notification Policy Access but is not allowed to manage the global zen state
+ * calls {@link NotificationManager#setInterruptionFilter}.
+ *
+ * <p>When the {@code zenMode} is {@link Global#ZEN_MODE_OFF}, an existing implicit rule will be
+ * deactivated (if there is no implicit rule, the call will be ignored). For other modes, the
+ * rule's interruption filter will match the supplied {@code zenMode}. The policy of the last
+ * call to {@link NotificationManager#setNotificationPolicy} will be used (or, if never called,
+ * the global policy).
+ *
+ * <p>The created rule is owned by the calling package, but it has neither a
+ * {@link ConditionProviderService} nor an associated
+ * {@link AutomaticZenRule#configurationActivity}.
+ *
+ * @param zenMode one of the {@code Global#ZEN_MODE_x} values
+ */
+ void applyGlobalZenModeAsImplicitZenRule(String callingPkg, int callingUid, int zenMode) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "applyGlobalZenModeAsImplicitZenRule called with flag off!");
+ return;
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return;
+ }
+ 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,
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ } 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);
+ newConfig.automaticRules.put(rule.id, rule);
+ }
+ rule.zenMode = zenMode;
+ rule.snoozing = false;
+ rule.condition = new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_activated),
+ Condition.STATE_TRUE);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null,
+ "applyGlobalZenModeAsImplicitZenRule",
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ }
+ }
+
+ /**
+ * Create (or update) an "implicit" {@link ZenRule} when an app that has Notification Policy
+ * Access but is not allowed to manage the global zen state calls
+ * {@link NotificationManager#setNotificationPolicy}.
+ *
+ * <p>The created rule is owned by the calling package and has the {@link ZenPolicy}
+ * corresponding to the supplied {@code policy}, but it has neither a
+ * {@link ConditionProviderService} nor an associated
+ * {@link AutomaticZenRule#configurationActivity}. Its zen mode will be set to
+ * {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
+ */
+ void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
+ NotificationManager.Policy policy) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
+ return;
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return;
+ }
+ ZenModeConfig newConfig = mConfig.copy();
+ ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (rule == null) {
+ rule = newImplicitZenRule(callingPkg);
+ rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ newConfig.automaticRules.put(rule.id, rule);
+ }
+ // TODO: b/308673679 - Keep user customization of this rule!
+ rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null,
+ "applyGlobalPolicyAsImplicitZenRule",
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ }
+
+ /**
+ * Returns the {@link Policy} associated to the "implicit" {@link ZenRule} of a package that has
+ * Notification Policy Access but is not allowed to manage the global zen state.
+ *
+ * <p>If the implicit rule doesn't exist, or it doesn't specify a {@link ZenPolicy} (because the
+ * app never called {@link NotificationManager#setNotificationPolicy}) then the default policy
+ * is returned (i.e. same as {@link #getNotificationPolicy}.
+ *
+ * <p>Any unset values in the {@link ZenPolicy} will be mapped to their current defaults.
+ */
+ @Nullable
+ Policy getNotificationPolicyFromImplicitZenRule(String callingPkg) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "getNotificationPolicyFromImplicitZenRule called with flag off!");
+ return getNotificationPolicy();
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return null;
+ }
+ ZenRule implicitRule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (implicitRule != null && implicitRule.zenPolicy != null) {
+ return mConfig.toNotificationPolicy(implicitRule.zenPolicy);
+ } else {
+ return getNotificationPolicy();
+ }
+ }
+ }
+
+ /**
+ * Creates an empty {@link ZenRule} to be used as the implicit rule for {@code pkg}.
+ * Both {@link ZenRule#zenMode} and {@link ZenRule#zenPolicy} are unset.
+ */
+ private ZenRule newImplicitZenRule(String pkg) {
+ ZenRule rule = new ZenRule();
+ rule.id = implicitRuleId(pkg);
+ rule.pkg = pkg;
+ rule.creationTime = System.currentTimeMillis();
+
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
+ rule.name = applicationInfo.loadLabel(mPm).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ // Should not happen, since it's the app calling us (?)
+ Log.w(TAG, "Package not found for creating implicit zen rule");
+ rule.name = "Unknown";
+ }
+ });
+
+ rule.condition = null;
+ rule.conditionId = new Uri.Builder()
+ .scheme(Condition.SCHEME)
+ .authority("android")
+ .appendPath("implicit")
+ .appendPath(pkg)
+ .build();
+ rule.enabled = true;
+ rule.modified = false;
+ rule.component = null;
+ rule.configurationActivity = null;
+ return rule;
+ }
+
+ private static String implicitRuleId(String forPackage) {
+ return "implicit_" + forPackage;
+ }
+
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
@@ -606,7 +806,7 @@ public class ZenModeHelper {
}
// update default rule (if locale changed, name of rule will change)
currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
"locale changed", callingUid, fromSystemOrSystemUi);
}
}
@@ -649,14 +849,20 @@ public class ZenModeHelper {
return null;
}
- private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+ private static void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
boolean isNew) {
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ rule.snoozing = false;
+ }
rule.name = automaticZenRule.getName();
rule.condition = null;
rule.conditionId = automaticZenRule.getConditionId();
rule.enabled = automaticZenRule.isEnabled();
rule.modified = automaticZenRule.isModified();
rule.zenPolicy = automaticZenRule.getZenPolicy();
+ if (Flags.modesApi()) {
+ rule.zenDeviceEffects = automaticZenRule.getDeviceEffects();
+ }
rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
rule.configurationActivity = automaticZenRule.getConfigurationActivity();
@@ -668,9 +874,6 @@ public class ZenModeHelper {
rule.pkg = pkg;
}
- if (rule.enabled != automaticZenRule.isEnabled()) {
- rule.snoozing = false;
- }
if (Flags.modesApi()) {
rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
rule.iconResId = automaticZenRule.getIconResId();
@@ -679,7 +882,7 @@ public class ZenModeHelper {
}
}
- protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
+ private static AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
AutomaticZenRule azr;
if (Flags.modesApi()) {
azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
@@ -688,6 +891,7 @@ public class ZenModeHelper {
.setIconResId(rule.iconResId)
.setType(rule.type)
.setZenPolicy(rule.zenPolicy)
+ .setDeviceEffects(rule.zenDeviceEffects)
.setEnabled(rule.enabled)
.setInterruptionFilter(
NotificationManager.zenModeToInterruptionFilter(rule.zenMode))
@@ -706,6 +910,27 @@ public class ZenModeHelper {
return azr;
}
+ @SuppressLint("MissingPermission")
+ void scheduleActivationBroadcast(String pkg, @UserIdInt int userId, String ruleId,
+ boolean activated) {
+ if (CompatChanges.isChangeEnabled(
+ SEND_ACTIVATION_AZR_STATUSES, pkg, UserHandle.of(userId))) {
+ dispatchOnAutomaticRuleStatusChanged(userId, pkg, ruleId, activated
+ ? AUTOMATIC_RULE_STATUS_ACTIVATED
+ : AUTOMATIC_RULE_STATUS_DEACTIVATED);
+ } else {
+ dispatchOnAutomaticRuleStatusChanged(
+ userId, pkg, ruleId, AUTOMATIC_RULE_STATUS_UNKNOWN);
+ }
+ }
+
+ void scheduleEnabledBroadcast(String pkg, @UserIdInt int userId, String ruleId,
+ boolean enabled) {
+ dispatchOnAutomaticRuleStatusChanged(userId, pkg, ruleId, enabled
+ ? AUTOMATIC_RULE_STATUS_ENABLED
+ : AUTOMATIC_RULE_STATUS_DISABLED);
+ }
+
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/, callingUid,
@@ -1002,7 +1227,7 @@ public class ZenModeHelper {
dispatchOnPolicyChanged();
}
updateConfigAndZenModeLocked(config, reason, setRingerMode, callingUid,
- fromSystemOrSystemUi);
+ fromSystemOrSystemUi, true);
mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
return true;
} catch (SecurityException e) {
@@ -1019,13 +1244,31 @@ public class ZenModeHelper {
*/
@GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
- boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
+ boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi,
+ boolean sendBroadcasts) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS);
// Store (a copy of) all config and zen mode info prior to any changes taking effect
ZenModeEventLogger.ZenModeInfo prevInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
if (!config.equals(mConfig)) {
+ // schedule broadcasts
+ if (Flags.modesApi() && sendBroadcasts) {
+ for (ZenRule rule : config.automaticRules.values()) {
+ ZenRule original = mConfig.automaticRules.get(rule.id);
+ if (original != null) {
+ if (original.enabled != rule.enabled) {
+ scheduleEnabledBroadcast(
+ rule.getPkg(), config.user, rule.id, rule.enabled);
+ }
+ if (original.isAutomaticActive() != rule.isAutomaticActive()) {
+ scheduleActivationBroadcast(
+ rule.getPkg(), config.user, rule.id, rule.isAutomaticActive());
+ }
+ }
+ }
+ }
+
mConfig = config;
dispatchOnConfigChanged();
updateConsolidatedPolicy(reason);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 25b7ca146ff1..dcac8c98d19f 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -7,8 +7,6 @@ flag {
bug: "290381858"
}
-
-
flag {
name: "polite_notifications"
namespace: "systemui"
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index a8cba532435d..f985b5b64e21 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -355,15 +355,7 @@ public class PersistentDataBlockService extends SystemService {
private boolean computeAndWriteDigestLocked() {
byte[] digest = computeDigestLocked(null);
if (digest != null) {
- FileChannel channel;
- try {
- channel = getBlockOutputChannel();
- } catch (IOException e) {
- Slog.e(TAG, "partition not available?", e);
- return false;
- }
-
- try {
+ try (FileChannel channel = getBlockOutputChannel()) {
ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES);
buf.put(digest);
buf.flip();
@@ -424,8 +416,7 @@ public class PersistentDataBlockService extends SystemService {
@VisibleForTesting
void formatPartitionLocked(boolean setOemUnlockEnabled) {
- try {
- FileChannel channel = getBlockOutputChannel();
+ try (FileChannel channel = getBlockOutputChannel()) {
// Format the data selectively.
//
// 1. write header, set length = 0
@@ -471,8 +462,7 @@ public class PersistentDataBlockService extends SystemService {
private void doSetOemUnlockEnabledLocked(boolean enabled) {
- try {
- FileChannel channel = getBlockOutputChannel();
+ try (FileChannel channel = getBlockOutputChannel()) {
channel.position(getBlockDeviceSize() - 1);
@@ -554,14 +544,6 @@ public class PersistentDataBlockService extends SystemService {
return (int) -maxBlockSize;
}
- FileChannel channel;
- try {
- channel = getBlockOutputChannel();
- } catch (IOException e) {
- Slog.e(TAG, "partition not available?", e);
- return -1;
- }
-
ByteBuffer headerAndData = ByteBuffer.allocate(
data.length + HEADER_SIZE + DIGEST_SIZE_BYTES);
headerAndData.put(new byte[DIGEST_SIZE_BYTES]);
@@ -574,7 +556,7 @@ public class PersistentDataBlockService extends SystemService {
return -1;
}
- try {
+ try (FileChannel channel = getBlockOutputChannel()) {
channel.write(headerAndData);
channel.force(true);
} catch (IOException e) {
@@ -831,8 +813,7 @@ public class PersistentDataBlockService extends SystemService {
if (!mIsWritable) {
return;
}
- try {
- FileChannel channel = getBlockOutputChannel();
+ try (FileChannel channel = getBlockOutputChannel()) {
channel.position(offset);
channel.write(dataBuffer);
channel.force(true);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 21284a05ddad..659c36c56047 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -45,11 +45,11 @@ import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 50ed3b1859df..af6a002e66c4 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -111,6 +111,11 @@ public class ApkChecksums {
private static final Certificate[] EMPTY_CERTIFICATE_ARRAY = {};
/**
+ * Arbitrary size restriction for the signature, used to sign the checksums.
+ */
+ private static final int MAX_SIGNATURE_SIZE_BYTES = 35 * 1024;
+
+ /**
* Check back in 1 second after we detected we needed to wait for the APK to be fully available.
*/
private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000;
@@ -260,6 +265,10 @@ public class ApkChecksums {
*/
public static @NonNull Certificate[] verifySignature(Checksum[] checksums, byte[] signature)
throws NoSuchAlgorithmException, IOException, SignatureException {
+ if (signature == null || signature.length > MAX_SIGNATURE_SIZE_BYTES) {
+ throw new SignatureException("Invalid signature");
+ }
+
final byte[] blob;
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
writeChecksums(os, checksums);
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 21610c9510ac..bdcec3a33221 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -58,6 +58,9 @@ import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
@@ -68,9 +71,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java
index d38b83fa6758..f3f64c5010ee 100644
--- a/services/core/java/com/android/server/pm/AppsFilterUtils.java
+++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java
@@ -27,15 +27,15 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedSparseSetArray;
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3ed9f029c73b..11a6d1b8f9a4 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -60,6 +60,7 @@ import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatureArrays;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell;
+import static com.android.server.pm.parsing.PackageInfoUtils.getDeprecatedSignatures;
import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import android.Manifest;
@@ -122,6 +123,12 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -140,12 +147,6 @@ import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -1525,11 +1526,14 @@ public class ComputerEngine implements Computer {
ai.volumeUuid = ps.getVolumeUuid();
ai.storageUuid = StorageManager.convert(ai.volumeUuid);
ai.setVersionCode(ps.getVersionCode());
+ ai.targetSdkVersion = ps.getTargetSdkVersion();
ai.flags = ps.getFlags();
ai.privateFlags = ps.getPrivateFlags();
pi.applicationInfo = PackageInfoUtils.generateDelegateApplicationInfo(
ai, flags, state, userId);
pi.signingInfo = ps.getSigningInfo();
+ pi.signatures = getDeprecatedSignatures(pi.signingInfo.getSigningDetails(), flags);
+ pi.setArchiveTimeMillis(state.getArchiveTimeMillis());
if (DEBUG_PACKAGE_INFO) {
Log.v(TAG, "ps.pkg is n/a for ["
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 8bf903ae0b13..65c6329587a5 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.content.pm.Flags.sdkLibIndependence;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
@@ -578,6 +579,12 @@ final class DeletePackageHelper {
? null
: ps.getUserStateOrDefault(nextUserId).getArchiveState();
+ // Preserve firstInstallTime in case of DELETE_KEEP_DATA
+ // For full uninstalls, reset firstInstallTime to 0 as if it has never been installed
+ final long firstInstallTime = (flags & DELETE_KEEP_DATA) == 0
+ ? 0
+ : ps.getUserStateOrDefault(nextUserId).getFirstInstallTimeMillis();
+
ps.setUserState(nextUserId,
ps.getCeDataInode(nextUserId),
ps.getDeDataInode(nextUserId),
@@ -597,9 +604,13 @@ final class DeletePackageHelper {
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashScreenTheme*/,
- 0 /*firstInstallTime*/,
+ firstInstallTime,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET,
archiveState);
+
+ if ((flags & DELETE_ARCHIVE) != 0) {
+ ps.modifyUserState(nextUserId).setArchiveTimeMillis(System.currentTimeMillis());
+ }
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f8d27f1bad1c..d46d55977d2f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -154,6 +154,11 @@ import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -177,11 +182,6 @@ import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.FileIntegrityService;
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index d5dacce3c8ce..eff6157380c0 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -22,6 +22,7 @@ import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
+import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -61,6 +62,7 @@ import android.os.Process;
import android.os.SELinux;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ExceptionUtils;
import android.util.Slog;
import com.android.internal.R;
@@ -398,7 +400,45 @@ public class PackageArchiver {
packageName)));
}
- mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
+ int draftSessionId;
+ try {
+ draftSessionId = createDraftSession(packageName, installerPackage, userId);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof IOException) {
+ throw ExceptionUtils.wrap((IOException) e.getCause());
+ } else {
+ throw e;
+ }
+ }
+
+ mPm.mHandler.post(
+ () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId));
+ }
+
+ private int createDraftSession(String packageName, String installerPackage, int userId) {
+ PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ sessionParams.setAppPackageName(packageName);
+ sessionParams.installFlags = INSTALL_UNARCHIVE_DRAFT;
+ int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
+ // Handles case of repeated unarchival calls for the same package.
+ int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid,
+ sessionParams,
+ userId);
+ if (existingSessionId != PackageInstaller.SessionInfo.INVALID_ID) {
+ return existingSessionId;
+ }
+
+ int sessionId = Binder.withCleanCallingIdentity(
+ () -> mPm.mInstallerService.createSessionInternal(
+ sessionParams,
+ installerPackage, mContext.getAttributionTag(),
+ installerUid,
+ userId));
+ // TODO(b/297358628) Also cleanup sessions upon device restart.
+ mPm.mHandler.postDelayed(() -> mPm.mInstallerService.cleanupDraftIfUnclaimed(sessionId),
+ getUnarchiveForegroundTimeout());
+ return sessionId;
}
/**
@@ -461,7 +501,7 @@ public class PackageArchiver {
cloudDrawable.getIntrinsicWidth(),
cloudDrawable.getIntrinsicHeight());
LayerDrawable layerDrawable =
- new LayerDrawable(new Drawable[] {appIconDrawable, cloudDrawable});
+ new LayerDrawable(new Drawable[]{appIconDrawable, cloudDrawable});
final int iconSize = mContext.getSystemService(
ActivityManager.class).getLauncherLargeIconSize();
Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize);
@@ -487,10 +527,11 @@ public class PackageArchiver {
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND},
conditional = true)
private void unarchiveInternal(String packageName, UserHandle userHandle,
- String installerPackage) {
+ String installerPackage, int unarchiveId) {
int userId = userHandle.getIdentifier();
Intent unarchiveIntent = new Intent(Intent.ACTION_UNARCHIVE_PACKAGE);
unarchiveIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ unarchiveIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_ID, unarchiveId);
unarchiveIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_PACKAGE_NAME, packageName);
unarchiveIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_ALL_USERS,
userId == UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 98eee4dc3b1d..af43a8bec832 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -18,7 +18,9 @@ package com.android.server.pm;
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
@@ -633,17 +635,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
+ "to use a data loader");
}
+ // Draft sessions cannot be created through the public API.
+ params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE_DRAFT;
return createSessionInternal(params, installerPackageName, callingAttributionTag,
- userId);
+ Binder.getCallingUid(), userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
- private int createSessionInternal(SessionParams params, String installerPackageName,
- String installerAttributionTag, int userId)
+ int createSessionInternal(SessionParams params, String installerPackageName,
+ String installerAttributionTag, int callingUid, int userId)
throws IOException {
- final int callingUid = Binder.getCallingUid();
final Computer snapshot = mPm.snapshotComputer();
snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
@@ -692,7 +695,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// initiatingPackageName
installerPackageName = SHELL_PACKAGE_NAME;
} else {
- if (callingUid != Process.SYSTEM_UID) {
+ if (callingUid != SYSTEM_UID) {
// The supplied installerPackageName must always belong to the calling app.
mAppOps.checkPackage(callingUid, installerPackageName);
}
@@ -707,6 +710,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+ params.installFlags &= ~PackageManager.INSTALL_ARCHIVED;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
&& !mPm.isCallerVerifier(snapshot, callingUid)) {
@@ -903,6 +907,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ int requestedInstallerPackageUid = INVALID_UID;
+ if (requestedInstallerPackageName != null) {
+ requestedInstallerPackageUid = snapshot.getPackageUid(requestedInstallerPackageName,
+ 0 /* flags */, userId);
+ }
+ if (requestedInstallerPackageUid == INVALID_UID) {
+ // Requested installer package is invalid, reset it
+ requestedInstallerPackageName = null;
+ }
+
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
@@ -923,8 +937,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
+ final int existingDraftSessionId =
+ getExistingDraftSessionId(requestedInstallerPackageUid, params, userId);
- sessionId = allocateSessionIdLocked();
+ sessionId = existingDraftSessionId != SessionInfo.INVALID_ID ? existingDraftSessionId
+ : allocateSessionIdLocked();
}
final long createdMillis = System.currentTimeMillis();
@@ -945,15 +962,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
params.forceQueryableOverride = false;
}
}
- int requestedInstallerPackageUid = INVALID_UID;
- if (requestedInstallerPackageName != null) {
- requestedInstallerPackageUid = snapshot.getPackageUid(requestedInstallerPackageName,
- 0 /* flags */, userId);
- }
- if (requestedInstallerPackageUid == INVALID_UID) {
- // Requested installer package is invalid, reset it
- requestedInstallerPackageName = null;
- }
final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) {
@@ -988,6 +996,68 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
return sessionId;
}
+ int getExistingDraftSessionId(int installerUid,
+ @NonNull SessionParams sessionParams, int userId) {
+ synchronized (mSessions) {
+ return getExistingDraftSessionIdInternal(installerUid, sessionParams, userId);
+ }
+ }
+
+ @GuardedBy("mSessions")
+ private int getExistingDraftSessionIdInternal(int installerUid,
+ SessionParams sessionParams, int userId) {
+ String appPackageName = sessionParams.appPackageName;
+ if (!Flags.archiving() || installerUid == INVALID_UID || appPackageName == null) {
+ return SessionInfo.INVALID_ID;
+ }
+
+ PackageStateInternal ps = mPm.snapshotComputer().getPackageStateInternal(appPackageName,
+ SYSTEM_UID);
+ if (ps == null || !PackageArchiver.isArchived(ps.getUserStateOrDefault(userId))) {
+ return SessionInfo.INVALID_ID;
+ }
+
+ // If unarchiveId is present we match based on it. If unarchiveId is missing we
+ // choose a draft session too to ensure we don't end up with duplicate sessions
+ // if the installer doesn't set this field.
+ if (sessionParams.unarchiveId > 0) {
+ PackageInstallerSession session = mSessions.get(sessionParams.unarchiveId);
+ if (session != null
+ && isValidDraftSession(session, appPackageName, installerUid, userId)) {
+ return session.sessionId;
+ }
+
+ return SessionInfo.INVALID_ID;
+ }
+
+ for (int i = 0; i < mSessions.size(); i++) {
+ PackageInstallerSession session = mSessions.valueAt(i);
+ if (session != null
+ && isValidDraftSession(session, appPackageName, installerUid, userId)) {
+ return session.sessionId;
+ }
+ }
+
+ return SessionInfo.INVALID_ID;
+ }
+
+ private boolean isValidDraftSession(@NonNull PackageInstallerSession session,
+ @NonNull String appPackageName, int installerUid, int userId) {
+ return (session.getInstallFlags() & PackageManager.INSTALL_UNARCHIVE_DRAFT) != 0
+ && appPackageName.equals(session.params.appPackageName)
+ && session.userId == userId
+ && installerUid == session.getInstallerUid();
+ }
+
+ void cleanupDraftIfUnclaimed(int sessionId) {
+ synchronized (mSessions) {
+ PackageInstallerSession session = mPm.mInstallerService.getSession(sessionId);
+ if (session != null && (session.getInstallFlags() & INSTALL_UNARCHIVE_DRAFT) != 0) {
+ session.abandon();
+ }
+ }
+ }
+
private boolean isStagedInstallerAllowed(String installerName) {
return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName);
}
@@ -1053,7 +1123,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
private boolean checkOpenSessionAccess(final PackageInstallerSession session) {
- if (session == null) {
+ if (session == null
+ || (session.getInstallFlags() & PackageManager.INSTALL_UNARCHIVE_DRAFT) != 0) {
return false;
}
if (isCallingUidOwner(session)) {
@@ -1248,10 +1319,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
final PackageInstallerSession session = mSessions.valueAt(i);
SessionInfo info =
- session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID);
+ session.generateInfoForCaller(false /*withIcon*/, SYSTEM_UID);
if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
&& session.userId == userId && !session.hasParentSessionId()
- && isCallingUidOwner(session)) {
+ && isCallingUidOwner(session)
+ && (session.getInstallFlags() & PackageManager.INSTALL_UNARCHIVE_DRAFT)
+ == 0) {
result.add(info);
}
}
@@ -1602,7 +1675,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
PackageInstallerSession session = null;
try {
var sessionId = createSessionInternal(params, installerPackageName,
- null /*installerAttributionTag*/, userId);
+ null /*installerAttributionTag*/, Binder.getCallingUid(), userId);
session = openSessionInternal(sessionId);
session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/, metadata.toByteArray(),
null /*signature*/);
@@ -2017,7 +2090,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// we don't scrub the data here as this is sent only to the installer several
// privileged system packages
sendSessionUpdatedBroadcast(
- session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID),
+ session.generateInfoForCaller(false/*icon*/, SYSTEM_UID),
session.userId);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4b466be8476b..47d1df5df1c0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1612,7 +1612,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
} catch (IOException | NoSuchAlgorithmException | SignatureException e) {
- throw new IllegalArgumentException("Can't verify signature", e);
+ throw new IllegalArgumentException("Can't verify signature: " + e.getMessage(), e);
+ }
+ }
+
+ for (Checksum checksum : checksums) {
+ if (checksum.getValue() == null
+ || checksum.getValue().length > Checksum.MAX_CHECKSUM_SIZE_BYTES) {
+ throw new IllegalArgumentException("Invalid checksum.");
}
}
@@ -2241,31 +2248,39 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean isInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null || psi.getPkg() == null) {
+ return true;
+ }
+ if (psi.getPkg().isUpdatableSystem()) {
+ return true;
+ }
+ if (mOriginalInstallerUid == Process.ROOT_UID) {
+ Slog.w(TAG, "Overriding updatableSystem because the installer is root: "
+ + psi.getPackageName());
+ return true;
+ }
+ return false;
+ }
+
/**
* Check if this package can be installed archived.
*/
- private static boolean isArchivedInstallationAllowed(String packageName) {
- final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
- if (existingPkgSetting == null) {
+ private static boolean isArchivedInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null) {
return true;
}
-
return false;
}
/**
* Checks if the package can be installed on IncFs.
*/
- private static boolean isIncrementalInstallationAllowed(String packageName) {
- final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
- if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) {
+ private static boolean isIncrementalInstallationAllowed(PackageStateInternal psi) {
+ if (psi == null || psi.getPkg() == null) {
return true;
}
-
- return !existingPkgSetting.isSystem()
- && !existingPkgSetting.isUpdatedSystemApp();
+ return !psi.isSystem() && !psi.isUpdatedSystemApp();
}
/**
@@ -3364,6 +3379,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Split " + apk.getSplitName() + " was defined multiple times");
}
+ if (!apk.isUpdatableSystem()) {
+ if (mOriginalInstallerUid == Process.ROOT_UID) {
+ Slog.w(TAG, "Overriding updatableSystem because the installer is root for: "
+ + apk.getPackageName());
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Non updatable system package can't be installed or updated");
+ }
+ }
+
// Use first package to define unknown values
if (mPackageName == null) {
mPackageName = apk.getPackageName();
@@ -3438,8 +3463,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(mPackageName);
+
+ if (!isInstallationAllowed(existingPkgSetting)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID,
+ "Installation of this package is not allowed.");
+ }
+
if (isArchivedInstallation()) {
- if (!isArchivedInstallationAllowed(mPackageName)) {
+ if (!isArchivedInstallationAllowed(existingPkgSetting)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
"Archived installation of this package is not allowed.");
@@ -3455,7 +3489,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (isIncrementalInstallation()) {
- if (!isIncrementalInstallationAllowed(mPackageName)) {
+ if (!isIncrementalInstallationAllowed(existingPkgSetting)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
"Incremental installation of this package is not allowed.");
@@ -3552,21 +3586,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.setDontKillApp(false);
}
+ boolean existingSplitReplacedOrRemoved = false;
// Inherit splits if not overridden.
if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
for (int i = 0; i < existing.getSplitNames().length; i++) {
final String splitName = existing.getSplitNames()[i];
final File splitFile = new File(existing.getSplitApkPaths()[i]);
final boolean splitRemoved = removeSplitList.contains(splitName);
- if (!stagedSplits.contains(splitName) && !splitRemoved) {
+ final boolean splitReplaced = stagedSplits.contains(splitName);
+ if (!splitReplaced && !splitRemoved) {
inheritFileLocked(splitFile);
// Collect the requiredSplitTypes and staged splitTypes from splits
CollectionUtils.addAll(requiredSplitTypes,
existing.getRequiredSplitTypes()[i]);
CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]);
+ } else {
+ existingSplitReplacedOrRemoved = true;
}
}
}
+ if (existingSplitReplacedOrRemoved
+ && (params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ // Some splits are being replaced or removed. Make sure the app is restarted.
+ params.setDontKillApp(false);
+ }
// Inherit compiled oat directory.
final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index e7499680b9a2..b281808e89b6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -47,13 +47,13 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import java.io.IOException;
@@ -758,6 +758,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
return snapshot().isPackageQuarantinedForUser(packageName, userId);
}
+ @Override
+ public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) {
+ return snapshot().isPackageStoppedForUser(packageName, userId);
+ }
+
@NonNull
@Override
@Deprecated
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 434c00afa8b2..ec3823f365b6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,8 @@ import com.android.internal.app.ResolverActivity;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -233,8 +235,6 @@ import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.pkg.mutate.PackageStateWrite;
import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
@@ -3105,7 +3105,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
- String callingPackage, int callingUid, int userId, String callingMethod) {
+ boolean quarantined, String callingPackage, int callingUid, int userId,
+ String callingMethod) {
if (callingUid == Process.ROOT_UID
// Need to compare app-id to allow system dialogs access on secondary users
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
@@ -3120,8 +3121,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
- callingMethod);
+ if (quarantined) {
+ final boolean hasQuarantineAppsPerm = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.QUARANTINE_APPS) == PERMISSION_GRANTED;
+ // TODO: b/305256093 - In order to facilitate testing, temporarily allowing apps
+ // with SUSPEND_APPS permission to quarantine apps. Remove this once the testing
+ // is done and this is no longer needed.
+ if (!hasQuarantineAppsPerm) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
@@ -6136,9 +6149,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
final int callingUid = Binder.getCallingUid();
- final Computer snapshot = snapshotComputer();
- enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
- "setPackagesSuspendedAsUser");
boolean quarantined = false;
if (Flags.quarantinedEnabled()) {
if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) {
@@ -6149,6 +6159,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
quarantined = callingPackage.equals(wellbeingPkg);
}
}
+ final Computer snapshot = snapshotComputer();
+ enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid,
+ userId, "setPackagesSuspendedAsUser");
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
quarantined);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index bcb7bdebf9bf..cd3416348153 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -92,6 +92,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -104,7 +105,6 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java
index 241f14390d5e..651bc5fac06a 100644
--- a/services/core/java/com/android/server/pm/PackageProperty.java
+++ b/services/core/java/com/android/server/pm/PackageProperty.java
@@ -31,8 +31,8 @@ import android.os.Binder;
import android.os.UserHandle;
import android.util.ArrayMap;
+import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedComponent;
import java.util.ArrayList;
import java.util.Iterator;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 3cf5481589fe..72090f24a2ed 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -91,13 +91,15 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
@IntDef({
INSTALL_PERMISSION_FIXED,
UPDATE_AVAILABLE,
- FORCE_QUERYABLE_OVERRIDE
+ FORCE_QUERYABLE_OVERRIDE,
+ SCANNED_AS_STOPPED_SYSTEM_APP
})
public @interface Flags {
}
private static final int INSTALL_PERMISSION_FIXED = 1;
private static final int UPDATE_AVAILABLE = 1 << 1;
private static final int FORCE_QUERYABLE_OVERRIDE = 1 << 2;
+ private static final int SCANNED_AS_STOPPED_SYSTEM_APP = 1 << 3;
}
private int mBooleans;
@@ -768,6 +770,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
onChanged();
}
+ void setArchiveTimeMillis(long value, int userId) {
+ modifyUserState(userId).setArchiveTimeMillis(value);
+ onChanged();
+ }
+
boolean getInstalled(int userId) {
return readUserState(userId).isInstalled();
}
@@ -881,6 +888,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
onChanged();
}
+ public PackageSetting setScannedAsStoppedSystemApp(boolean stop) {
+ setBoolean(Booleans.SCANNED_AS_STOPPED_SYSTEM_APP, stop);
+ onChanged();
+ return this;
+ }
+
boolean getNotLaunched(int userId) {
return readUserState(userId).isNotLaunched();
}
@@ -1559,6 +1572,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return (getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0;
}
+ @Override
+ public boolean isScannedAsStoppedSystemApp() {
+ return getBoolean(Booleans.SCANNED_AS_STOPPED_SYSTEM_APP);
+ }
+
// Code below generated by codegen v1.0.23.
@@ -1707,10 +1725,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
@DataClass.Generated(
- time = 1698188444364L,
+ time = 1700251133016L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\npublic com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index b055a3ffd688..639d6d78f953 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -44,6 +44,7 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.parsing.PackageCacher;
@@ -52,7 +53,6 @@ import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import java.io.File;
import java.util.Collections;
@@ -409,12 +409,17 @@ final class RemovePackageHelper {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Updating installed state to false because of DELETE_KEEP_DATA");
}
+ final boolean isArchive = (flags & PackageManager.DELETE_ARCHIVE) != 0;
+ final long currentTimeMillis = System.currentTimeMillis();
for (int userId : outInfo.mRemovedUsers) {
if (DEBUG_REMOVE) {
final boolean wasInstalled = deletedPs.getInstalled(userId);
Slog.d(TAG, " user " + userId + ": " + wasInstalled + " => " + false);
}
deletedPs.setInstalled(/* installed= */ false, userId);
+ if (isArchive) {
+ deletedPs.modifyUserState(userId).setArchiveTimeMillis(currentTimeMillis);
+ }
}
}
// make sure to preserve per-user installed state if this removal was just
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 7ea9e3f529d0..22ee963cf9a2 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -73,6 +73,11 @@ import android.util.jar.StrictJarFile;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -82,11 +87,6 @@ import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedArraySet;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6338965e4a80..107dc761ceda 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -89,6 +89,10 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -113,10 +117,6 @@ import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.SuspendParams;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.resolution.ComponentResolver;
import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
@@ -373,6 +373,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
private static final String ATTR_ARCHIVE_ICON_PATH = "icon-path";
private static final String ATTR_ARCHIVE_MONOCHROME_ICON_PATH = "monochrome-icon-path";
+ private static final String ATTR_ARCHIVE_TIME = "archive-time";
+
private final Handler mHandler;
private final PackageManagerTracedLock mLock;
@@ -948,6 +950,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ret.getPkgState().setUpdatedSystemApp(false);
ret.setTargetSdkVersion(p.getTargetSdkVersion());
ret.setRestrictUpdateHash(p.getRestrictUpdateHash());
+ ret.setScannedAsStoppedSystemApp(p.isScannedAsStoppedSystemApp());
}
mDisabledSysPackages.remove(name);
return ret;
@@ -1162,6 +1165,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
Slog.i(PackageManagerService.TAG, "Stopping system package " + pkgName, e);
}
pkgSetting.setStopped(true, installUserId);
+ pkgSetting.setScannedAsStoppedSystemApp(true);
}
if (sharedUser != null) {
pkgSetting.setAppId(sharedUser.mAppId);
@@ -1929,6 +1933,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ATTR_SPLASH_SCREEN_THEME);
final long firstInstallTime = parser.getAttributeLongHex(null,
ATTR_FIRST_INSTALL_TIME, 0);
+ final long archiveTime = parser.getAttributeLongHex(null,
+ ATTR_ARCHIVE_TIME, 0);
final int minAspectRatio = parser.getAttributeInt(null,
ATTR_MIN_ASPECT_RATIO,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
@@ -2016,7 +2022,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
firstInstallTime != 0 ? firstInstallTime
: origFirstInstallTimes.getOrDefault(name, 0L),
minAspectRatio, archiveState);
-
+ ps.setArchiveTimeMillis(archiveTime, userId);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -2379,6 +2385,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
serializer.attributeLongHex(null, ATTR_FIRST_INSTALL_TIME,
ustate.getFirstInstallTimeMillis());
+ serializer.attributeLongHex(null, ATTR_ARCHIVE_TIME,
+ ustate.getArchiveTimeMillis());
if (ustate.getUninstallReason()
!= PackageManager.UNINSTALL_REASON_UNKNOWN) {
serializer.attributeInt(null, ATTR_UNINSTALL_REASON,
@@ -3072,6 +3080,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
serializer.attributeBytesBase64(null, "restrictUpdateHash",
pkg.getRestrictUpdateHash());
}
+ serializer.attributeBoolean(null, "scannedAsStoppedSystemApp",
+ pkg.isScannedAsStoppedSystemApp());
if (pkg.getLegacyNativeLibraryPath() != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath());
}
@@ -3140,6 +3150,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
serializer.attributeBytesBase64(null, "restrictUpdateHash",
pkg.getRestrictUpdateHash());
}
+ serializer.attributeBoolean(null, "scannedAsStoppedSystemApp",
+ pkg.isScannedAsStoppedSystemApp());
if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
@@ -3873,6 +3885,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
int targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0);
byte[] restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash",
null);
+ boolean isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null,
+ "scannedAsStoppedSystemApp", false);
int pkgFlags = 0;
int pkgPrivateFlags = 0;
@@ -3893,7 +3907,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
.setCpuAbiOverride(cpuAbiOverrideStr)
.setLongVersionCode(versionCode)
.setTargetSdkVersion(targetSdkVersion)
- .setRestrictUpdateHash(restrictUpdateHash);
+ .setRestrictUpdateHash(restrictUpdateHash)
+ .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp);
long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3988,6 +4003,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
String appMetadataFilePath = null;
int targetSdkVersion = 0;
byte[] restrictUpdateHash = null;
+ boolean isScannedAsStoppedSystemApp = false;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -4028,6 +4044,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
categoryHint = parser.getAttributeInt(null, "categoryHint",
ApplicationInfo.CATEGORY_UNDEFINED);
appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath");
+ isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null,
+ "scannedAsStoppedSystemApp", false);
String domainSetIdString = parser.getAttributeValue(null, "domainSetId");
@@ -4174,7 +4192,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
.setLoadingCompletedTime(loadingCompletedTime)
.setAppMetadataFilePath(appMetadataFilePath)
.setTargetSdkVersion(targetSdkVersion)
- .setRestrictUpdateHash(restrictUpdateHash);
+ .setRestrictUpdateHash(restrictUpdateHash)
+ .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4970,6 +4989,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pw.print(prefix); pw.print(" privateFlags="); printFlags(pw,
privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
}
+ if (!pkg.isUpdatableSystem()) {
+ pw.print(prefix); pw.print(" updatableSystem=false");
+ pw.println();
+ }
if (pkg.hasPreserveLegacyExternalStorage()) {
pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true");
pw.println();
@@ -4988,6 +5011,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pw.append(prefix).append(" queriesIntents=")
.println(ps.getPkg().getQueriesIntents());
}
+ pw.print(prefix); pw.print(" scannedAsStoppedSystemApp=");
+ pw.println(ps.isScannedAsStoppedSystemApp());
pw.print(prefix); pw.print(" supportsScreens=[");
boolean first = true;
if (pkg.isSmallScreensSupported()) {
@@ -5268,6 +5293,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
date.setTime(pus.getFirstInstallTimeMillis());
pw.println(sdf.format(date));
+ pw.print(" archiveTime=");
+ date.setTime(pus.getArchiveTimeMillis());
+ pw.println(sdf.format(date));
+
pw.print(" uninstallReason=");
pw.println(userState.getUninstallReason());
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 9376259c08b1..dddc6b0fbb7a 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -25,13 +25,13 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProcessImpl;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
index adac68b25749..215646778778 100644
--- a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
+++ b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
@@ -29,9 +29,9 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java
index 651578dc5deb..f120763a9e1a 100644
--- a/services/core/java/com/android/server/pm/UserJourneyLogger.java
+++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java
@@ -23,6 +23,7 @@ import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
import static com.android.internal.util.FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN;
@@ -245,6 +246,9 @@ public class UserJourneyLogger {
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
case USER_TYPE_PROFILE_CLONE:
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE;
+ case USER_TYPE_PROFILE_PRIVATE:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_PRIVATE;
default:
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81a570f0e7a5..4e14c908b01b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1388,10 +1388,32 @@ public class UserManagerService extends IUserManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
+ // QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL is only allowed for managed-profiles
+ if (dontAskCredential) {
+ UserInfo userInfo;
+ synchronized (mUsersLock) {
+ userInfo = getUserInfo(userId);
+ }
+ if (!userInfo.isManagedProfile()) {
+ throw new IllegalArgumentException("Invalid flags: " + flags
+ + ". Can't skip credential check for the user");
+ }
+ }
if (enableQuietMode) {
setQuietModeEnabled(userId, true /* enableQuietMode */, target, callingPackage);
return true;
}
+ if (android.os.Flags.allowPrivateProfile()) {
+ final UserProperties userProperties = getUserPropertiesInternal(userId);
+ if (userProperties != null
+ && userProperties.isAuthAlwaysRequiredToDisableQuietMode()) {
+ if (onlyIfCredentialNotRequired) {
+ return false;
+ }
+ showConfirmCredentialToDisableQuietMode(userId, target);
+ return false;
+ }
+ }
final boolean hasUnifiedChallenge =
mLockPatternUtils.isManagedProfileWithUnifiedChallenge(userId);
if (hasUnifiedChallenge) {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 29e0c35d88bd..7da76c18216e 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -193,6 +193,7 @@ public final class UserTypeFactory {
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+ .setAuthAlwaysRequiredToDisableQuietMode(false)
.setCredentialShareableWithParent(true));
}
@@ -292,7 +293,8 @@ public final class UserTypeFactory {
.setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
- .setCredentialShareableWithParent(false)
+ .setCredentialShareableWithParent(true)
+ .setAuthAlwaysRequiredToDisableQuietMode(true)
.setMediaSharedWithParent(false)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index c26cf1c9a113..926e0188c624 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -52,6 +52,17 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageArchiver;
@@ -65,17 +76,6 @@ import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
@@ -152,6 +152,7 @@ public class PackageInfoUtils {
info.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
info.firstInstallTime = firstInstallTime;
info.lastUpdateTime = lastUpdateTime;
+ info.setArchiveTimeMillis(state.getArchiveTimeMillis());
if ((flags & PackageManager.GET_GIDS) != 0) {
info.gids = gids;
}
@@ -238,21 +239,7 @@ public class PackageInfoUtils {
}
final SigningDetails signingDetails = pkg.getSigningDetails();
- // deprecated method of getting signing certificates
- if ((flags & PackageManager.GET_SIGNATURES) != 0) {
- if (signingDetails.hasPastSigningCertificates()) {
- // Package has included signing certificate rotation information. Return the oldest
- // cert so that programmatic checks keep working even if unaware of key rotation.
- info.signatures = new Signature[1];
- info.signatures[0] = signingDetails.getPastSigningCertificates()[0];
- } else if (signingDetails.hasSignatures()) {
- // otherwise keep old behavior
- int numberOfSigs = signingDetails.getSignatures().length;
- info.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.getSignatures(), 0, info.signatures, 0,
- numberOfSigs);
- }
- }
+ info.signatures = getDeprecatedSignatures(signingDetails, flags);
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
@@ -358,6 +345,30 @@ public class PackageInfoUtils {
return info;
}
+ /**
+ * Retrieve the deprecated {@link PackageInfo.signatures} field of signing certificates
+ */
+ public static Signature[] getDeprecatedSignatures(SigningDetails signingDetails, long flags) {
+ if ((flags & PackageManager.GET_SIGNATURES) == 0) {
+ return null;
+ }
+ if (signingDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ Signature[] signatures = new Signature[1];
+ signatures[0] = signingDetails.getPastSigningCertificates()[0];
+ return signatures;
+ } else if (signingDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = signingDetails.getSignatures().length;
+ Signature[] signatures = new Signature[numberOfSigs];
+ System.arraycopy(signingDetails.getSignatures(), 0, signatures, 0,
+ numberOfSigs);
+ return signatures;
+ }
+ return null;
+ }
+
private static void updateApplicationInfo(ApplicationInfo ai, long flags,
PackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 97d526d1c44e..8916efd7aa5a 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -20,8 +20,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedComponent;
/**
* For exposing internal fields to the rest of the server, enforcing that any overridden state from
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index e2acc17e94c4..0eb2bbde5886 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -29,16 +29,16 @@ import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.incremental.IncrementalManager;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 75dd67d97620..c8ac6982071b 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -50,6 +50,19 @@ import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
@@ -61,27 +74,15 @@ import com.android.server.pm.pkg.AndroidPackageSplit;
import com.android.server.pm.pkg.AndroidPackageSplitImpl;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.pm.pkg.component.ParsedAttributionImpl;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProcessImpl;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
@@ -396,7 +397,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
// an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
// and never assign this, so initialize this to true for those cases.
private long mBooleans = Booleans.ENABLED;
- private long mBooleans2;
+ private long mBooleans2 = Booleans2.UPDATABLE_SYSTEM;
@NonNull
private Set<String> mKnownActivityEmbeddingCerts = emptySet();
// Derived fields
@@ -3305,7 +3306,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
this.instrumentations = ParsingUtils.createTypedInterfaceList(in,
ParsedInstrumentationImpl.CREATOR);
this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
- this.processes = in.readHashMap(ParsedProcess.class.getClassLoader());
+ this.processes = in.readHashMap(ParsedProcessImpl.class.getClassLoader());
this.metaData = in.readBundle(boot);
this.volumeUuid = sForInternedString.unparcel(in);
this.signingDetails = in.readParcelable(boot, android.content.pm.SigningDetails.class);
@@ -3450,6 +3451,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public boolean isUpdatableSystem() {
+ return getBoolean2(Booleans2.UPDATABLE_SYSTEM);
+ }
+
+ @Override
public boolean isFactoryTest() {
return getBoolean(Booleans.FACTORY_TEST);
}
@@ -3521,6 +3527,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public PackageImpl setUpdatableSystem(boolean value) {
+ return setBoolean2(Booleans2.UPDATABLE_SYSTEM, value);
+ }
+
+ @Override
public PackageImpl setFactoryTest(boolean value) {
setBoolean(Booleans.FACTORY_TEST, value);
return this;
@@ -3731,10 +3742,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@LongDef({
STUB,
APEX,
+ UPDATABLE_SYSTEM,
})
public @interface Flags {}
private static final long STUB = 1L;
private static final long APEX = 1L << 1;
+ private static final long UPDATABLE_SYSTEM = 1L << 2;
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index aeaff6dd1458..85f8f7609c2a 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -73,6 +73,8 @@ public interface ParsedPackage extends AndroidPackage {
ParsedPackage setApex(boolean isApex);
+ ParsedPackage setUpdatableSystem(boolean value);
+
ParsedPackage markNotActivitiesAsNotExportedIfSingleUser();
ParsedPackage setOdm(boolean odm);
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index c81d6d7d0918..07ff0ee049e1 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -28,9 +28,9 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.pkg.PackageState;
-import com.android.server.pm.pkg.component.ParsedPermission;
import libcore.util.EmptyArray;
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 6764e087ff04..8bd2d94667f9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -70,6 +70,7 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
@@ -119,6 +120,8 @@ import com.android.internal.compat.IPlatformCompat;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IntPair;
@@ -142,8 +145,6 @@ import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
@@ -5327,7 +5328,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
IOnPermissionsChangeListener callback = mPermissionListeners
.getBroadcastItem(i);
try {
- callback.onPermissionsChanged(uid);
+ callback.onPermissionsChanged(uid,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
} catch (RemoteException e) {
Log.e(TAG, "Permission listener is dead", e);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
index 3a617041d55e..61677eb06fe9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
@@ -18,10 +18,11 @@ package com.android.server.pm.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+
import java.util.Collection;
/**
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 91854fda09d3..99819c82c5df 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -47,17 +47,17 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.R;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import java.security.PublicKey;
@@ -1393,6 +1393,13 @@ public interface AndroidPackage {
/** @hide */
boolean isApex();
+
+ /**
+ * @see R.styleable#AndroidManifestApplication_updatableSystem
+ * @hide
+ */
+ boolean isUpdatableSystem();
+
/**
* @see ApplicationInfo#enabled
* @see R.styleable#AndroidManifestApplication_enabled
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index e7137bbd7969..10b59c7230f6 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -456,4 +456,12 @@ public interface PackageState {
@Immutable.Ignore
@Nullable
byte[] getRestrictUpdateHash();
+
+ /**
+ * whether the package has been scanned as a stopped system app. A package will be
+ * scanned in the stopped state if it is a system app that has a launcher entry and is
+ * <b>not</b> exempted by {@code <initial-package-state>} tag, and is not an APEX
+ * @hide
+ */
+ boolean isScannedAsStoppedSystemApp();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 1d2c5ec2d081..20fcdb564b96 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -22,7 +22,7 @@ import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.util.SparseArray;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
/** @hide */
public class PackageStateUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 2a81a86d20f6..8eb346608732 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -256,4 +256,10 @@ public interface PackageUserState {
* @hide
*/
boolean dataExists();
+
+ /**
+ * Timestamp of when the app is archived on the user.
+ * @hide
+ */
+ long getArchiveTimeMillis();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 2f4ad2d8fcc6..defd3437c14b 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -206,4 +206,9 @@ class PackageUserStateDefault implements PackageUserStateInternal {
public boolean dataExists() {
return true;
}
+
+ @Override
+ public long getArchiveTimeMillis() {
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index a76a7ce03170..c0ea7cc0aba2 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -135,6 +135,8 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
@Nullable
private ArchiveState mArchiveState;
+ private @CurrentTimeMillisLong long mArchiveTimeMillis;
+
@NonNull
final SnapshotCache<PackageUserStateImpl> mSnapshot;
@@ -187,6 +189,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
? null : other.mComponentLabelIconOverrideMap.snapshot();
mFirstInstallTimeMillis = other.mFirstInstallTimeMillis;
mArchiveState = other.mArchiveState;
+ mArchiveTimeMillis = other.mArchiveTimeMillis;
mSnapshot = new SnapshotCache.Sealed<>();
}
@@ -602,8 +605,6 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
/**
* Sets the value for {@link #getArchiveState()}.
- *
- * @hide
*/
@NonNull
public PackageUserStateImpl setArchiveState(@NonNull ArchiveState archiveState) {
@@ -612,6 +613,16 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
return this;
}
+ /**
+ * Sets the timestamp when the app is archived on this user.
+ */
+ @NonNull
+ public PackageUserStateImpl setArchiveTimeMillis(@CurrentTimeMillisLong long value) {
+ mArchiveTimeMillis = value;
+ onChanged();
+ return this;
+ }
+
@NonNull
@Override
public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
@@ -800,6 +811,11 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
}
@DataClass.Generated.Member
+ public @CurrentTimeMillisLong long getArchiveTimeMillis() {
+ return mArchiveTimeMillis;
+ }
+
+ @DataClass.Generated.Member
public @NonNull SnapshotCache<PackageUserStateImpl> getSnapshot() {
return mSnapshot;
}
@@ -876,6 +892,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
&& mFirstInstallTimeMillis == that.mFirstInstallTimeMillis
&& watchableEquals(that.mWatchable)
&& Objects.equals(mArchiveState, that.mArchiveState)
+ && mArchiveTimeMillis == that.mArchiveTimeMillis
&& snapshotEquals(that.mSnapshot);
}
@@ -906,15 +923,16 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
_hash = 31 * _hash + Long.hashCode(mFirstInstallTimeMillis);
_hash = 31 * _hash + watchableHashCode();
_hash = 31 * _hash + Objects.hashCode(mArchiveState);
+ _hash = 31 * _hash + Long.hashCode(mArchiveTimeMillis);
_hash = 31 * _hash + snapshotHashCode();
return _hash;
}
@DataClass.Generated(
- time = 1694196888631L,
+ time = 1699917927942L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nprivate @android.annotation.CurrentTimeMillisLong long mArchiveTimeMillis\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index cd3583b814a4..fe80f743ffc3 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -27,7 +27,7 @@ import android.os.Debug;
import android.util.DebugUtils;
import android.util.Slog;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
/** @hide */
public class PackageUserStateUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
index 063f577bce5f..411bdede315f 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -22,8 +22,8 @@ import android.content.pm.SigningDetails;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.server.pm.permission.LegacyPermissionState;
-import com.android.server.pm.pkg.component.ParsedProcess;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
index 1deb8d055e20..1964df0853fd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
@@ -19,6 +19,14 @@ package com.android.server.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+
/**
* Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
* be removed once all post-parsing mutation is moved to parsing.
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
index a8fb79a52837..041edaa98e63 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
@@ -29,6 +29,9 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 68d5428d6604..f02790189cc0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -36,7 +36,7 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.server.pm.pkg.parsing.ParsingUtils;
@@ -49,7 +49,6 @@ import java.util.Set;
* @hide
**/
@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity,
Parcelable {
@@ -133,7 +132,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
* should be invisible to user and user should not know or see it.
*/
@NonNull
- static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
+ public static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
ParsedActivityImpl activity = new ParsedActivityImpl();
activity.setPackageName(packageName);
@@ -700,7 +699,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
time = 1669437519576L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
- inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+ inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index ee793c8b2f87..5709cbb09f93 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -48,6 +48,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
index 167aba301f35..cfed19aa0934 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -250,7 +251,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par
time = 1643723578605L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java",
- inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+ inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
index ed9aa2e6860a..d3fb29b8aa66 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -25,6 +25,8 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
index b59f511afa57..62b994724346 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
@@ -22,6 +22,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import com.android.internal.util.DataClass;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
index 98e94c5214f0..411220ae42e8 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
@@ -26,6 +26,7 @@ import android.content.res.XmlResourceParser;
import android.util.ArraySet;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
index f8d678ee39ac..512e5c7023c7 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
@@ -32,6 +32,8 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
index 8a0d356925d4..7bfad14d669a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
@@ -26,6 +26,7 @@ import android.os.Parcelable;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
index c63a68975588..9792a91fb699 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -26,6 +26,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
index 5b6375d6b365..ab9404310078 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
@@ -23,6 +23,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.DataClass;
/**
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
index 4f0a504b659f..5e67bbf4ab0b 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import android.util.TypedValue;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
index c670e7c1b4f7..f322eef8c3a3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
@@ -25,6 +25,7 @@ import android.os.Parcelable;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
index f52ad1393878..6c22f825bab9 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -31,6 +31,8 @@ import android.os.Build;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
index 59075deb258c..afe37bc3274c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.DataClass;
/**
@@ -174,7 +175,7 @@ public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements
time = 1642132854167L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java",
- inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.server.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+ inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
index 4c831d36c9e3..69e33c8f281e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -24,6 +24,8 @@ import android.text.TextUtils;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -119,8 +121,8 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP
this.requestRes = in.readInt();
this.protectionLevel = in.readInt();
this.tree = in.readBoolean();
- this.parsedPermissionGroup = in.readParcelable(ParsedPermissionGroup.class.getClassLoader(),
- ParsedPermissionGroupImpl.class);
+ this.parsedPermissionGroup = in.readParcelable(
+ ParsedPermissionGroupImpl.class.getClassLoader(), ParsedPermissionGroupImpl.class);
this.knownCerts = sForStringSet.unparcel(in);
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
index c6d1775307f8..0f2b49b8541c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -31,6 +31,8 @@ import android.util.EventLog;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
index 6d52f656b2e4..40e3670b9261 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
@@ -25,7 +25,7 @@ import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -35,7 +35,6 @@ import java.util.Set;
/** @hide */
@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
genBuilder = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class ParsedProcessImpl implements ParsedProcess, Parcelable {
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
index 4f4c2d5019f3..766fb90cbfa0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -27,6 +27,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
index 6f4b4c84a07e..81a3c17e2bb4 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
@@ -28,6 +28,7 @@ import android.os.PatternMatcher;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -301,7 +302,7 @@ public class ParsedProviderImpl extends ParsedMainComponentImpl implements Parse
time = 1642560323360L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java",
- inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+ inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
index 37bed15ba1d7..b66db4f9ced4 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -34,6 +34,7 @@ import android.os.PatternMatcher;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
index 47e993cb02be..ca8c45d1383c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
@@ -26,6 +26,8 @@ import android.os.Parcelable;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index c15266fc4cbc..1b421841f166 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -32,6 +32,7 @@ import android.content.res.XmlResourceParser;
import android.os.Build;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
index 9b89373bbb90..78377a836651 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 699ccbdc5a83..099c67654250 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -32,18 +32,18 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import java.security.PublicKey;
import java.util.List;
@@ -345,6 +345,8 @@ public interface ParsingPackage {
ParsingPackage setStaticSharedLibraryVersion(long staticSharedLibraryVersion);
+ ParsingPackage setUpdatableSystem(boolean value);
+
ParsingPackage setLargeScreensSupported(int supportsLargeScreens);
ParsingPackage setNormalScreensSupported(int supportsNormalScreens);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 061698a929d6..e4594c58f40f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -89,6 +89,19 @@ import android.util.apk.ApkSignatureVerifier;
import com.android.internal.R;
import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.SharedUidMigration;
@@ -98,29 +111,17 @@ import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
-import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.pm.pkg.component.ParsedAttributionUtils;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
import com.android.server.pm.pkg.component.ParsedIntentInfoUtils;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProcessUtils;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderUtils;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceUtils;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.split.DefaultSplitAssetLoader;
import com.android.server.pm.split.SplitAssetDependencyLoader;
@@ -1001,12 +1002,16 @@ public class ParsingPackageUtils {
return sharedUserResult;
}
+ final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
+ "updatableSystem", true);
+
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
- .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
+ .setUpdatableSystem(updatableSystem);
boolean foundApp = false;
final int depth = parser.getDepth();
@@ -2842,7 +2847,7 @@ public class ParsingPackageUtils {
String taskAffinity = result.getResult();
// Build custom App Details activity info instead of parsing it from xml
- return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
+ return input.success(ParsedActivityImpl.makeAppDetailsActivity(packageName,
pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
pkg.isHardwareAccelerated()));
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 07512855d276..2cfffb3b185d 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -30,9 +30,9 @@ import android.os.Parcelable;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.Parcelling;
import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 0ceda421913d..532a7f8f893f 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -45,6 +45,12 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.Computer;
@@ -57,13 +63,7 @@ import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
@@ -943,6 +943,12 @@ public class ComponentResolver extends ComponentResolverLocked implements
return false;
}
+ if (packageState.isSystem()) {
+ // A system app can be considered in the stopped state only if it was originally
+ // scanned in the stopped state.
+ return packageState.isScannedAsStoppedSystemApp() &&
+ packageState.getUserStateOrDefault(userId).isStopped();
+ }
return packageState.getUserStateOrDefault(userId).isStopped();
}
}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
index b8e4c8d2a51f..0f12ee1b52fb 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -25,11 +25,11 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import java.io.PrintWriter;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 80cde73ecf1f..2bc926c6d840 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -28,6 +28,11 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
import com.android.server.pm.UserManagerService;
@@ -36,11 +41,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.utils.WatchableImpl;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
index 0c84f4c53dfe..add33b25923e 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
@@ -24,13 +24,13 @@ import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerTracedLock;
import com.android.server.pm.UserManagerService;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import java.io.PrintWriter;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index adef808bd712..735f90faee5b 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -27,11 +27,11 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Patterns;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import java.util.List;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 3d4d4eca7f48..6150099b2945 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -48,6 +48,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.CollectionUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -60,7 +61,6 @@ import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a33e3532153b..61348b586aed 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -1312,7 +1312,8 @@ public final class PermissionPolicyService extends SystemService {
}
private boolean isTaskStartedFromLauncher(String currPkg, TaskInfo taskInfo) {
- return currPkg.equals(taskInfo.baseActivity.getPackageName())
+ return taskInfo.baseActivity != null
+ && currPkg.equals(taskInfo.baseActivity.getPackageName())
&& isLauncherIntent(taskInfo.baseIntent);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cf1036c03c83..1cf82bde633d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -32,7 +32,6 @@ import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
-import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
@@ -69,6 +68,7 @@ import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;
@@ -101,6 +101,7 @@ import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.IActivityManager;
import android.app.IUiModeManager;
import android.app.NotificationManager;
import android.app.ProgressDialog;
@@ -427,6 +428,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
ActivityManagerInternal mActivityManagerInternal;
+ IActivityManager mActivityManagerService;
ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
InputManager mInputManager;
@@ -549,7 +551,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mLidNavigationAccessibility;
int mShortPressOnPowerBehavior;
private boolean mShouldEarlyShortPressOnPower;
- private boolean mShouldEarlyShortPressOnStemPrimary;
+ boolean mShouldEarlyShortPressOnStemPrimary;
int mLongPressOnPowerBehavior;
long mLongPressOnPowerAssistantTimeoutMs;
int mVeryLongPressOnPowerBehavior;
@@ -578,6 +580,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private int mDoublePressOnStemPrimaryBehavior;
private int mTriplePressOnStemPrimaryBehavior;
private int mLongPressOnStemPrimaryBehavior;
+ private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
private boolean mHandleVolumeKeysInWM;
@@ -1070,7 +1073,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void powerPress(long eventTime, int count) {
+ private void powerPress(long eventTime, int count, int displayId) {
// SideFPS still needs to know about suppressed power buttons, in case it needs to block
// an auth attempt.
if (count == 1) {
@@ -1123,8 +1126,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
if (mDismissImeOnBackKeyPressed) {
- InputMethodManagerInternal.get().hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
+ // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
+ InputMethodManagerInternal.get().hideAllInputMethods(
+ SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, displayId);
} else {
shortPressPowerGoHome();
}
@@ -1563,7 +1567,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
? false
: mKeyguardDelegate.isShowing();
if (!keyguardActive) {
- switchRecentTask();
+ performStemPrimaryDoublePressSwitchToRecentTask();
}
break;
}
@@ -1672,11 +1676,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/**
* Load most recent task (expect current task) and bring it to the front.
*/
- private void switchRecentTask() {
- RecentTaskInfo targetTask = mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
+ void performStemPrimaryDoublePressSwitchToRecentTask() {
+ RecentTaskInfo targetTask = mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
if (targetTask == null) {
if (DEBUG_INPUT) {
- Slog.w(TAG, "No recent task available! Show watch face.");
+ Slog.w(TAG, "No recent task available! Show wallpaper.");
}
goHome();
return;
@@ -1695,7 +1699,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ targetTask.baseIntent);
}
try {
- ActivityManager.getService().startActivityFromRecents(targetTask.persistentId, null);
+ mActivityManagerService.startActivityFromRecents(targetTask.persistentId, null);
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Failed to start task " + targetTask.persistentId + " from recents", e);
}
@@ -2219,6 +2223,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
});
}
+
+ IActivityManager getActivityManagerService() {
+ return ActivityManager.getService();
+ }
}
/** {@inheritDoc} */
@@ -2233,6 +2241,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs = injector.getWindowManagerFuncs();
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mActivityManagerService = injector.getActivityManagerService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mInputManager = mContext.getSystemService(InputManager.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -2654,11 +2663,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onPress(long downTime) {
+ void onPress(long downTime, int displayId) {
if (mShouldEarlyShortPressOnPower) {
return;
}
- powerPress(downTime, 1 /*count*/);
+ powerPress(downTime, 1 /*count*/, displayId);
}
@Override
@@ -2688,14 +2697,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onMultiPress(long downTime, int count) {
- powerPress(downTime, count);
+ void onMultiPress(long downTime, int count, int displayId) {
+ powerPress(downTime, count, displayId);
}
@Override
- void onKeyUp(long eventTime, int count) {
+ void onKeyUp(long eventTime, int count, int displayId) {
if (mShouldEarlyShortPressOnPower && count == 1) {
- powerPress(eventTime, 1 /*pressCount*/);
+ powerPress(eventTime, 1 /*pressCount*/, displayId);
}
}
}
@@ -2719,7 +2728,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onPress(long downTime) {
+ void onPress(long downTime, int unusedDisplayId) {
mBackKeyHandled |= backKeyPress();
}
@@ -2748,7 +2757,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onPress(long downTime) {
+ void onPress(long downTime, int unusedDisplayId) {
if (mShouldEarlyShortPressOnStemPrimary) {
return;
}
@@ -2761,14 +2770,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onMultiPress(long downTime, int count) {
+ void onMultiPress(long downTime, int count, int unusedDisplayId) {
stemPrimaryPress(count);
}
@Override
- void onKeyUp(long eventTime, int count) {
- if (mShouldEarlyShortPressOnStemPrimary && count == 1) {
- stemPrimaryPress(1 /*pressCount*/);
+ void onKeyUp(long eventTime, int count, int unusedDisplayId) {
+ if (count == 1) {
+ // Save info about the most recent task on the first press of the stem key. This
+ // may be used later to switch to the most recent app using double press gesture.
+ // It is possible that we may navigate away from this task before the double
+ // press is detected, as a result of the first press, so we save the current
+ // most recent task before that happens.
+ mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
+ mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
+ if (mShouldEarlyShortPressOnStemPrimary) {
+ stemPrimaryPress(1 /*pressCount*/);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 047555ae491c..a060f504b809 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -66,10 +66,12 @@ public final class SingleKeyGestureDetector {
* SingleKeyRule rule =
* new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) {
* int getMaxMultiPressCount() { // maximum multi press count. }
- * void onPress(long downTime) { // short press behavior. }
+ * void onPress(long downTime, int displayId) { // short press behavior. }
* void onLongPress(long eventTime) { // long press behavior. }
* void onVeryLongPress(long eventTime) { // very long press behavior. }
- * void onMultiPress(long downTime, int count) { // multi press behavior. }
+ * void onMultiPress(long downTime, int count, int displayId) {
+ * // multi press behavior.
+ * }
* };
* </pre>
*/
@@ -114,11 +116,11 @@ public final class SingleKeyGestureDetector {
/**
* Called when short press has been detected.
*/
- abstract void onPress(long downTime);
+ abstract void onPress(long downTime, int displayId);
/**
* Callback when multi press (>= 2) has been detected.
*/
- void onMultiPress(long downTime, int count) {}
+ void onMultiPress(long downTime, int count, int displayId) {}
/**
* Returns the timeout in milliseconds for a long press.
*
@@ -148,10 +150,11 @@ public final class SingleKeyGestureDetector {
/**
* Callback executed upon each key up event that hasn't been processed by long press.
*
- * @param eventTime the timestamp of this event.
- * @param pressCount the number of presses detected leading up to this key up event.
+ * @param eventTime the timestamp of this event
+ * @param pressCount the number of presses detected leading up to this key up event
+ * @param displayId the display ID of the event
*/
- void onKeyUp(long eventTime, int pressCount) {}
+ void onKeyUp(long eventTime, int pressCount, int displayId) {}
@Override
public String toString() {
@@ -179,6 +182,10 @@ public final class SingleKeyGestureDetector {
}
}
+ private record MessageObject(SingleKeyRule activeRule, int keyCode, int pressCount,
+ int displayId) {
+ }
+
static SingleKeyGestureDetector get(Context context, Looper looper) {
SingleKeyGestureDetector detector = new SingleKeyGestureDetector(looper);
sDefaultLongPressTimeout = context.getResources().getInteger(
@@ -228,8 +235,9 @@ public final class SingleKeyGestureDetector {
mHandledByLongPress = true;
mHandler.removeMessages(MSG_KEY_LONG_PRESS);
mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
- final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
- mActiveRule);
+ MessageObject object = new MessageObject(mActiveRule, keyCode, /* pressCount= */ 1,
+ event.getDisplayId());
+ final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -275,15 +283,17 @@ public final class SingleKeyGestureDetector {
if (mKeyPressCounter == 1) {
if (mActiveRule.supportLongPress()) {
- final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
- mActiveRule);
+ MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
+ event.getDisplayId());
+ final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, mActiveRule.getLongPressTimeoutMs());
}
if (mActiveRule.supportVeryLongPress()) {
- final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0,
- mActiveRule);
+ MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
+ event.getDisplayId());
+ final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, mActiveRule.getVeryLongPressTimeoutMs());
}
@@ -299,8 +309,9 @@ public final class SingleKeyGestureDetector {
Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it"
+ " reached the max count " + mKeyPressCounter);
}
- final Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, keyCode,
- mKeyPressCounter, mActiveRule);
+ MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
+ event.getDisplayId());
+ final Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -339,9 +350,9 @@ public final class SingleKeyGestureDetector {
if (event.getKeyCode() == mActiveRule.mKeyCode) {
// key-up action should always be triggered if not processed by long press.
- Message msgKeyUp =
- mHandler.obtainMessage(
- MSG_KEY_UP, mActiveRule.mKeyCode, mKeyPressCounter, mActiveRule);
+ MessageObject object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
+ mKeyPressCounter, event.getDisplayId());
+ Message msgKeyUp = mHandler.obtainMessage(MSG_KEY_UP, object);
msgKeyUp.setAsynchronous(true);
mHandler.sendMessage(msgKeyUp);
@@ -350,8 +361,9 @@ public final class SingleKeyGestureDetector {
if (DEBUG) {
Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
}
- Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
- 1, mActiveRule);
+ object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
+ /* pressCount= */ 1, event.getDisplayId());
+ Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
mActiveRule = null;
@@ -360,8 +372,9 @@ public final class SingleKeyGestureDetector {
// This could be a multi-press. Wait a little bit longer to confirm.
if (mKeyPressCounter < mActiveRule.getMaxMultiPressCount()) {
- Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
- mKeyPressCounter, mActiveRule);
+ object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
+ mKeyPressCounter, event.getDisplayId());
+ Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
}
@@ -423,20 +436,23 @@ public final class SingleKeyGestureDetector {
@Override
public void handleMessage(Message msg) {
- final SingleKeyRule rule = (SingleKeyRule) msg.obj;
+ final MessageObject object = (MessageObject) msg.obj;
+ final SingleKeyRule rule = object.activeRule;
if (rule == null) {
Log.wtf(TAG, "No active rule.");
return;
}
- final int keyCode = msg.arg1;
- final int pressCount = msg.arg2;
+ final int keyCode = object.keyCode;
+ final int pressCount = object.pressCount;
+ final int displayId = object.displayId;
switch(msg.what) {
case MSG_KEY_UP:
if (DEBUG) {
- Log.i(TAG, "Detect key up " + KeyEvent.keyCodeToString(keyCode));
+ Log.i(TAG, "Detect key up " + KeyEvent.keyCodeToString(keyCode)
+ + " on display " + displayId);
}
- rule.onKeyUp(mLastDownTime, pressCount);
+ rule.onKeyUp(mLastDownTime, pressCount, displayId);
break;
case MSG_KEY_LONG_PRESS:
if (DEBUG) {
@@ -454,12 +470,12 @@ public final class SingleKeyGestureDetector {
case MSG_KEY_DELAYED_PRESS:
if (DEBUG) {
Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode)
- + ", count " + pressCount);
+ + " on display " + displayId + ", count " + pressCount);
}
if (pressCount == 1) {
- rule.onPress(mLastDownTime);
+ rule.onPress(mLastDownTime, displayId);
} else {
- rule.onMultiPress(mLastDownTime, pressCount);
+ rule.onMultiPress(mLastDownTime, pressCount, displayId);
}
break;
}
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index a0e91ad7cf45..94340ec26cba 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -2,4 +2,6 @@ michaelwr@google.com
santoscordon@google.com
philipjunker@google.com
-per-file ThermalManagerService.java=wvw@google.com
+per-file ThermalManagerService.java=file:/THERMAL_OWNERS
+per-file LowPowerStandbyController.java=qingxun@google.com
+per-file LowPowerStandbyControllerInternal.java=qingxun@google.com
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 99064bc1884d..d17207b8f261 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -28,6 +28,7 @@ import android.hardware.thermal.V1_0.ThermalStatusCode;
import android.hardware.thermal.V1_1.IThermalCallback;
import android.os.Binder;
import android.os.CoolingDevice;
+import android.os.Flags;
import android.os.Handler;
import android.os.HwBinder;
import android.os.IBinder;
@@ -181,7 +182,7 @@ public class ThermalManagerService extends SystemService {
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
- mTemperatureWatcher.updateSevereThresholds();
+ mTemperatureWatcher.updateThresholds();
mHalReady.set(true);
}
}
@@ -506,6 +507,20 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ public float[] getThermalHeadroomThresholds() {
+ if (!mHalReady.get()) {
+ throw new IllegalStateException("Thermal HAL connection is not initialized");
+ }
+ if (!Flags.allowThermalHeadroomThresholds()) {
+ throw new UnsupportedOperationException("Thermal headroom thresholds not enabled");
+ }
+ synchronized (mTemperatureWatcher.mSamples) {
+ return Arrays.copyOf(mTemperatureWatcher.mHeadroomThresholds,
+ mTemperatureWatcher.mHeadroomThresholds.length);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
dumpInternal(fd, pw, args);
}
@@ -580,6 +595,12 @@ public class ThermalManagerService extends SystemService {
mHalWrapper.getTemperatureThresholds(false, 0));
}
}
+ if (Flags.allowThermalHeadroomThresholds()) {
+ synchronized (mTemperatureWatcher.mSamples) {
+ pw.println("Temperature headroom thresholds:");
+ pw.println(Arrays.toString(mTemperatureWatcher.mHeadroomThresholds));
+ }
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -964,7 +985,14 @@ public class ThermalManagerService extends SystemService {
connectToHal();
}
if (mInstance != null) {
- Slog.i(TAG, "Thermal HAL AIDL service connected.");
+ try {
+ Slog.i(TAG, "Thermal HAL AIDL service connected with version "
+ + mInstance.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to read interface version from Thermal HAL", e);
+ connectToHal();
+ return;
+ }
registerThermalChangedCallback();
}
}
@@ -1440,26 +1468,55 @@ public class ThermalManagerService extends SystemService {
ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
@GuardedBy("mSamples")
+ float[] mHeadroomThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
+ @GuardedBy("mSamples")
private long mLastForecastCallTimeMillis = 0;
private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
@VisibleForTesting
long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
- void updateSevereThresholds() {
+ void updateThresholds() {
synchronized (mSamples) {
List<TemperatureThreshold> thresholds =
mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+ if (Flags.allowThermalHeadroomThresholds()) {
+ Arrays.fill(mHeadroomThresholds, Float.NaN);
+ }
for (int t = 0; t < thresholds.size(); ++t) {
TemperatureThreshold threshold = thresholds.get(t);
if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
continue;
}
- float temperature =
+ float severeThreshold =
threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
- if (!Float.isNaN(temperature)) {
- mSevereThresholds.put(threshold.name,
- threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]);
+ if (!Float.isNaN(severeThreshold)) {
+ mSevereThresholds.put(threshold.name, severeThreshold);
+ for (int severity = ThrottlingSeverity.LIGHT;
+ severity <= ThrottlingSeverity.SHUTDOWN; severity++) {
+ if (Flags.allowThermalHeadroomThresholds()
+ && threshold.hotThrottlingThresholds.length > severity) {
+ updateHeadroomThreshold(severity,
+ threshold.hotThrottlingThresholds[severity],
+ severeThreshold);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // For a older device with multiple SKIN sensors, we will set a severity's headroom
+ // threshold based on the minimum value of all as a workaround.
+ void updateHeadroomThreshold(int severity, float threshold, float severeThreshold) {
+ if (!Float.isNaN(threshold)) {
+ synchronized (mSamples) {
+ float headroom = normalizeTemperature(threshold, severeThreshold);
+ if (Float.isNaN(mHeadroomThresholds[severity])) {
+ mHeadroomThresholds[severity] = headroom;
+ } else {
+ float lastHeadroom = mHeadroomThresholds[severity];
+ mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom);
}
}
}
@@ -1541,15 +1598,13 @@ public class ThermalManagerService extends SystemService {
private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
@VisibleForTesting
- float normalizeTemperature(float temperature, float severeThreshold) {
- synchronized (mSamples) {
- float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
- if (temperature <= zeroNormalized) {
- return 0.0f;
- }
- float delta = temperature - zeroNormalized;
- return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
+ static float normalizeTemperature(float temperature, float severeThreshold) {
+ float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
+ if (temperature <= zeroNormalized) {
+ return 0.0f;
}
+ float delta = temperature - zeroNormalized;
+ return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
}
private static final int MINIMUM_SAMPLE_COUNT = 3;
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 851a3f7bdaba..83d7d72059b4 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -188,10 +188,12 @@ public class BatteryUsageStatsProvider {
}
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND,
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
getProcessBackgroundTimeMs(uid, realtimeUs))
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND,
- getProcessForegroundTimeMs(uid, realtimeUs));
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
+ getProcessForegroundTimeMs(uid, realtimeUs))
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
+ getProcessForegroundServiceTimeMs(uid, realtimeUs));
}
final int[] powerComponents = query.getPowerComponents();
@@ -295,10 +297,14 @@ public class BatteryUsageStatsProvider {
}
private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
- return (uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
+ return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
+ realtimeUs, BatteryStats.STATS_SINCE_CHARGED)
+ / 1000;
+ }
+
+ private long getProcessForegroundServiceTimeMs(BatteryStats.Uid uid, long realtimeUs) {
+ return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
realtimeUs, BatteryStats.STATS_SINCE_CHARGED)
- + uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
- realtimeUs, BatteryStats.STATS_SINCE_CHARGED))
/ 1000;
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 77290fd944eb..9f0a97523af8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -603,40 +603,51 @@ public class PowerStatsService extends SystemService {
@NonNull
private String getEnergyConsumerName(EnergyConsumer consumer,
EnergyConsumer[] energyConsumers) {
- if (consumer.type != EnergyConsumerType.OTHER) {
- StringBuilder sb = new StringBuilder();
- sb.append(energyConsumerTypeToString(consumer.type));
- boolean hasOrdinal = consumer.ordinal != 0;
- if (!hasOrdinal) {
- // See if any other EnergyConsumer of the same type has an ordinal
- for (EnergyConsumer aConsumer : energyConsumers) {
- if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
- hasOrdinal = true;
- break;
- }
+ StringBuilder sb = new StringBuilder();
+ switch (consumer.type) {
+ case EnergyConsumerType.BLUETOOTH:
+ sb.append("BLUETOOTH");
+ break;
+ case EnergyConsumerType.CPU_CLUSTER:
+ sb.append("CPU");
+ break;
+ case EnergyConsumerType.DISPLAY:
+ sb.append("DISPLAY");
+ break;
+ case EnergyConsumerType.GNSS:
+ sb.append("GNSS");
+ break;
+ case EnergyConsumerType.MOBILE_RADIO:
+ sb.append("MOBILE_RADIO");
+ break;
+ case EnergyConsumerType.WIFI:
+ sb.append("WIFI");
+ break;
+ case EnergyConsumerType.CAMERA:
+ sb.append("CAMERA");
+ break;
+ default:
+ if (consumer.name != null && !consumer.name.isBlank()) {
+ sb.append(consumer.name.toUpperCase(Locale.ENGLISH));
+ } else {
+ sb.append("CONSUMER_").append(consumer.type);
+ }
+ break;
+ }
+ boolean hasOrdinal = consumer.ordinal != 0;
+ if (!hasOrdinal) {
+ // See if any other EnergyConsumer of the same type has an ordinal
+ for (EnergyConsumer aConsumer : energyConsumers) {
+ if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
+ hasOrdinal = true;
+ break;
}
}
- if (hasOrdinal) {
- sb.append('/').append(consumer.ordinal);
- }
- return sb.toString();
- } else {
- return consumer.name;
}
- }
-
- private static String energyConsumerTypeToString(int type) {
- switch(type) {
- case EnergyConsumerType.BLUETOOTH: return "BLUETOOTH";
- case EnergyConsumerType.CPU_CLUSTER: return "CPU";
- case EnergyConsumerType.DISPLAY: return "DISPLAY";
- case EnergyConsumerType.GNSS: return "GNSS";
- case EnergyConsumerType.MOBILE_RADIO: return "MOBILE_RADIO";
- case EnergyConsumerType.WIFI: return "WIFI";
- case EnergyConsumerType.OTHER: return "";
- default:
- throw new IllegalStateException("Unrecognized EnergyConsumerType: " + type);
+ if (hasOrdinal) {
+ sb.append('/').append(consumer.ordinal);
}
+ return sb.toString();
}
/**
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 6d580e97d578..8b57f87f5df3 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -280,12 +280,12 @@ final class SpeechRecognitionManagerServiceImpl extends
return null;
}
- if (getSessionCountByUidLocked(callingUid) >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
+ if (getSessionCountByUidLocked(callingUid) == MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
Slog.w(TAG, "Number of sessions exceeded for uid: " + callingUid);
Counter.logIncrementWithUid(
"speech_recognition.value_exceed_session_count",
callingUid);
- return null;
+ // TODO(b/297249772): return null early to refuse the new connection
}
if (servicesForClient != null) {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index c1da58943062..940feb580a96 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -24,12 +24,14 @@ import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
import static android.hardware.graphics.common.Hdr.DOLBY_VISION;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_PROXY;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
import static android.net.NetworkTemplate.OEM_MANAGED_PAID;
@@ -488,6 +490,7 @@ public class StatsPullAtomService extends SystemService {
case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+ case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG:
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER:
case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER:
@@ -973,21 +976,33 @@ public class StatsPullAtomService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "Registering NetworkStats pullers with statsd");
}
+
+ boolean canQueryTypeProxy = canQueryNetworkStatsForTypeProxy();
+
// Initialize NetworkStats baselines.
- mNetworkStatsBaselines.addAll(
- collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
- mNetworkStatsBaselines.addAll(
- collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
- mNetworkStatsBaselines.addAll(
- collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
- mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
- FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
- mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
- FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
- mNetworkStatsBaselines.addAll(
- collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
- mNetworkStatsBaselines.addAll(
- collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER));
+ synchronized (mDataBytesTransferLock) {
+ mNetworkStatsBaselines.addAll(
+ collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
+ mNetworkStatsBaselines.addAll(
+ collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
+ mNetworkStatsBaselines.addAll(
+ collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
+ mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
+ mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
+ mNetworkStatsBaselines.addAll(
+ collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
+ mNetworkStatsBaselines.addAll(
+ collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER));
+ if (canQueryTypeProxy) {
+ mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG));
+ }
+ }
// Listen to subscription changes to record historical subscriptions that activated before
// pulling, this is used by {@code DATA_USAGE_BYTES_TRANSFER}.
@@ -1001,6 +1016,9 @@ public class StatsPullAtomService extends SystemService {
registerBytesTransferByTagAndMetered();
registerDataUsageBytesTransfer();
registerOemManagedBytesTransfer();
+ if (canQueryTypeProxy) {
+ registerProxyBytesTransferBackground();
+ }
}
private void initAndRegisterDeferredPullers() {
@@ -1171,6 +1189,18 @@ public class StatsPullAtomService extends SystemService {
}
break;
}
+ case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: {
+ final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(
+ new NetworkTemplate.Builder(MATCH_PROXY).build(), /*includeTags=*/true);
+ if (stats != null) {
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
+ new int[]{TRANSPORT_BLUETOOTH},
+ /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+ /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/true));
+ }
+ break;
+ }
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
@@ -1183,7 +1213,7 @@ public class StatsPullAtomService extends SystemService {
new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR},
/*slicedByFgbg=*/false, /*slicedByTag=*/true,
/*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- /*subInfo=*/null, OEM_MANAGED_ALL));
+ /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/false));
}
break;
}
@@ -1225,7 +1255,7 @@ public class StatsPullAtomService extends SystemService {
final NetworkStatsExt diff = new NetworkStatsExt(
removeEmptyEntries(item.stats.subtract(baseline.stats)), item.transports,
item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType,
- item.subInfo, item.oemManaged);
+ item.subInfo, item.oemManaged, item.isTypeProxy);
// If no diff, skip.
if (!diff.stats.iterator().hasNext()) continue;
@@ -1363,7 +1393,7 @@ public class StatsPullAtomService extends SystemService {
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
/*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- /*subInfo=*/null, oemManaged));
+ /*subInfo=*/null, oemManaged, /*isTypeProxy=*/false));
}
}
}
@@ -1392,6 +1422,21 @@ public class StatsPullAtomService extends SystemService {
}
/**
+ * Check if it is possible to query NetworkStats for TYPE_PROXY. This should only be possible
+ * if the build includes r.android.com/2828315
+ * @return true if querying for TYPE_PROXY is allowed
+ */
+ private static boolean canQueryNetworkStatsForTypeProxy() {
+ try {
+ new NetworkTemplate.Builder(MATCH_PROXY).build();
+ return true;
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Querying network stats for TYPE_PROXY is not allowed");
+ return false;
+ }
+ }
+
+ /**
* Create a snapshot of NetworkStats since boot for the given template, but add 1 bucket
* duration before boot as a buffer to ensure at least one full bucket will be included.
* Note that this should be only used to calculate diff since the snapshot might contains
@@ -1421,6 +1466,7 @@ public class StatsPullAtomService extends SystemService {
final NetworkStats nonTaggedStats =
NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+ queryNonTaggedStats.close();
if (!includeTags) return nonTaggedStats;
final android.app.usage.NetworkStats queryTaggedStats =
@@ -1429,6 +1475,7 @@ public class StatsPullAtomService extends SystemService {
currentTimeInMillis);
final NetworkStats taggedStats =
NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats);
+ queryTaggedStats.close();
return nonTaggedStats.add(taggedStats);
}
@@ -1448,7 +1495,7 @@ public class StatsPullAtomService extends SystemService {
ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
/*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo,
- OEM_MANAGED_ALL));
+ OEM_MANAGED_ALL, /*isTypeProxy=*/false));
}
}
return ret;
@@ -1598,6 +1645,19 @@ public class StatsPullAtomService extends SystemService {
);
}
+ private void registerProxyBytesTransferBackground() {
+ int tagId = FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG;
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+ .setAdditiveFields(new int[]{3, 4, 5, 6})
+ .build();
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ metadata,
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
private void registerBytesTransferByTagAndMetered() {
int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
index 7dbba0d4337d..512f0bf7ff98 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java
@@ -42,15 +42,17 @@ public class NetworkStatsExt {
public final int oemManaged;
@Nullable
public final SubInfo subInfo;
+ public final boolean isTypeProxy; // True if matching ConnectivityManager#TYPE_PROXY
public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) {
this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, OEM_MANAGED_ALL);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null,
+ OEM_MANAGED_ALL, /*isTypeProxy=*/false);
}
public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg,
boolean slicedByTag, boolean slicedByMetered, int ratType,
- @Nullable SubInfo subInfo, int oemManaged) {
+ @Nullable SubInfo subInfo, int oemManaged, boolean isTypeProxy) {
this.stats = stats;
// Sort transports array so that we can test for equality without considering order.
@@ -63,6 +65,7 @@ public class NetworkStatsExt {
this.ratType = ratType;
this.subInfo = subInfo;
this.oemManaged = oemManaged;
+ this.isTypeProxy = isTypeProxy;
}
/**
@@ -72,6 +75,6 @@ public class NetworkStatsExt {
return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg
&& slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered
&& ratType == other.ratType && Objects.equals(subInfo, other.subInfo)
- && oemManaged == other.oemManaged;
+ && oemManaged == other.oemManaged && isTypeProxy == other.isTypeProxy;
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3fd832376d2b..7c51e7b84132 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1836,12 +1836,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void hideCurrentInputMethodForBubbles() {
+ public void hideCurrentInputMethodForBubbles(int displayId) {
enforceStatusBarService();
final long token = Binder.clearCallingIdentity();
try {
- InputMethodManagerInternal.get().hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_BUBBLES);
+ // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
+ InputMethodManagerInternal.get().hideAllInputMethods(
+ SoftInputShowHideReason.HIDE_BUBBLES, displayId);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
index 5c37680af745..17d327e94d4d 100644
--- a/services/core/java/com/android/server/timedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -7,10 +7,7 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
- }
- ],
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
+ },
{
"name": "FrameworksTimeServicesTests"
}
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 63dd7b42f23b..358618a71cbc 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -7,15 +7,15 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "FrameworksTimeServicesTests"
}
],
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
"name": "CtsLocationTimeZoneManagerHostTest"
- },
- {
- "name": "FrameworksTimeServicesTests"
}
]
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index fed6e7ee4686..b2e808ac8e95 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -117,16 +117,16 @@ abstract class Vibration {
static final class CallerInfo {
public final VibrationAttributes attrs;
public final int uid;
- public final int displayId;
+ public final int deviceId;
public final String opPkg;
public final String reason;
- CallerInfo(@NonNull VibrationAttributes attrs, int uid, int displayId,
- String opPkg, String reason) {
+ CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
+ String reason) {
Objects.requireNonNull(attrs);
this.attrs = attrs;
this.uid = uid;
- this.displayId = displayId;
+ this.deviceId = deviceId;
this.opPkg = opPkg;
this.reason = reason;
}
@@ -138,14 +138,14 @@ abstract class Vibration {
CallerInfo that = (CallerInfo) o;
return Objects.equals(attrs, that.attrs)
&& uid == that.uid
- && displayId == that.displayId
+ && deviceId == that.deviceId
&& Objects.equals(opPkg, that.opPkg)
&& Objects.equals(reason, that.reason);
}
@Override
public int hashCode() {
- return Objects.hash(attrs, uid, displayId, opPkg, reason);
+ return Objects.hash(attrs, uid, deviceId, opPkg, reason);
}
@Override
@@ -153,7 +153,7 @@ abstract class Vibration {
return "CallerInfo{"
+ " uid=" + uid
+ ", opPkg=" + opPkg
- + ", displayId=" + displayId
+ + ", deviceId=" + deviceId
+ ", attrs=" + attrs
+ ", reason=" + reason
+ '}';
@@ -267,8 +267,8 @@ abstract class Vibration {
mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
String callerInfoStr = String.format(Locale.ROOT,
- " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
- mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+ " | %s (uid=%d, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId,
mCallerInfo.attrs.usageToString(),
AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 7f55836598b5..839c2075fa8c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -61,7 +61,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -166,7 +165,6 @@ final class VibrationSettings {
final MyUidObserver mUidObserver;
@VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
- final VirtualDeviceListener mVirtualDeviceListener;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -180,6 +178,8 @@ final class VibrationSettings {
@GuardedBy("mLock")
@Nullable
private PowerManagerInternal mPowerManagerInternal;
+ @Nullable
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
@GuardedBy("mLock")
private boolean mVibrateInputDevices;
@@ -207,8 +207,6 @@ final class VibrationSettings {
mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new MyUidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
- mVirtualDeviceListener = new VirtualDeviceListener();
-
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -272,13 +270,6 @@ final class VibrationSettings {
}
});
- VirtualDeviceManagerInternal vdm = LocalServices.getService(
- VirtualDeviceManagerInternal.class);
- if (vdm != null) {
- vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
- vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
- }
-
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
@@ -414,8 +405,14 @@ final class VibrationSettings {
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
- if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(callerInfo.uid,
- callerInfo.displayId)) {
+
+ if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT
+ && callerInfo.deviceId != Context.DEVICE_ID_INVALID) {
+ return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ }
+
+ if (callerInfo.deviceId == Context.DEVICE_ID_INVALID
+ && isAppRunningOnAnyVirtualDevice(callerInfo.uid)) {
return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
@@ -794,6 +791,15 @@ final class VibrationSettings {
return out;
}
+ private boolean isAppRunningOnAnyVirtualDevice(int uid) {
+ if (mVirtualDeviceManagerInternal == null) {
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ return mVirtualDeviceManagerInternal != null
+ && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(uid);
+ }
+
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
final class SettingsContentObserver extends ContentObserver {
@@ -853,73 +859,4 @@ final class VibrationSettings {
}
}
}
-
- /**
- * Implementation of Virtual Device listeners for the changes of virtual displays and of apps
- * running on any virtual device.
- */
- final class VirtualDeviceListener implements
- VirtualDeviceManagerInternal.VirtualDisplayListener,
- VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener {
- @GuardedBy("mLock")
- private final Set<Integer> mVirtualDisplays = new HashSet<>();
- @GuardedBy("mLock")
- private final Set<Integer> mAppsOnVirtualDevice = new HashSet<>();
-
-
- @Override
- public void onVirtualDisplayCreated(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.add(displayId);
- }
- }
-
- @Override
- public void onVirtualDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.remove(displayId);
- }
- }
-
-
- @Override
- public void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids) {
- synchronized (mLock) {
- mAppsOnVirtualDevice.clear();
- mAppsOnVirtualDevice.addAll(allRunningUids);
- }
- }
-
- /**
- * @param uid: uid of the calling app.
- * @param displayId: the id of a Display.
- * @return Returns true if:
- * <ul>
- * <li> the displayId is valid, and it's owned by a virtual device.</li>
- * <li> the displayId is invalid, and the calling app (uid) is running on a virtual
- * device.</li>
- * </ul>
- */
- public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- // The default display is the primary physical display on the phone.
- return false;
- }
-
- synchronized (mLock) {
- if (displayId == Display.INVALID_DISPLAY) {
- // There is no Display object associated with the Context of calling
- // {@link SystemVibratorManager}, checking the calling UID instead.
- return mAppsOnVirtualDevice.contains(uid);
- } else {
- // Other valid display IDs representing valid logical displays will be
- // checked
- // against the active virtual displays set built with the registered
- // {@link VirtualDisplayListener}.
- return mVirtualDisplays.contains(displayId);
- }
- }
- }
-
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ace7777c9b58..cf33cc5f43bd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -63,7 +63,6 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,7 +384,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return false;
}
AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
- new Vibration.CallerInfo(attrs, uid, Display.DEFAULT_DISPLAY, opPkg,
+ new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
null), effects);
mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
updateAlwaysOnLocked(alwaysOnVibration);
@@ -397,16 +396,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
- public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect,
+ public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- vibrateWithPermissionCheck(uid, displayId, opPkg, effect, attrs, reason, token);
+ vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
}
@Override // Binder call
public void performHapticFeedback(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
- performHapticFeedbackInternal(uid, displayId, opPkg, constant, always, reason, token);
+ performHapticFeedbackInternal(uid, deviceId, opPkg, constant, always, reason, token);
}
/**
@@ -417,7 +416,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@VisibleForTesting
@Nullable
HalVibration performHapticFeedbackInternal(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
if (hapticVibrationProvider == null) {
@@ -433,7 +432,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
constant, /* bypassVibrationIntensitySetting= */ always);
- return vibrateWithoutPermissionCheck(uid, displayId, opPkg, combinedVibration, attrs,
+ return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
}
@@ -444,7 +443,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
@VisibleForTesting
@Nullable
- HalVibration vibrateWithPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
@@ -452,24 +451,24 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
attrs = fixupVibrationAttributes(attrs, effect);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE, "vibrate");
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
try {
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
+ private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
if (token == null) {
@@ -482,7 +481,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
HalVibration vib = new HalVibration(token, effect,
- new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
+ new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason));
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -1558,10 +1557,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private ExternalVibrationHolder(ExternalVibration externalVibration) {
super(externalVibration.getToken(), new Vibration.CallerInfo(
externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
- Display.INVALID_DISPLAY,
- externalVibration.getPackage(), null));
+ // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
+ // 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;
@@ -1974,8 +1972,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
synchronized (mLock) {
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
vibHolder.callerInfo);
@@ -2184,7 +2180,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, combined, attrs,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
commonOptions.description, deathBinder);
maybeWaitOnVibration(vib, commonOptions);
}
@@ -2241,7 +2237,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, constant,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
/* always= */ commonOptions.force, /* reason= */ commonOptions.description,
deathBinder);
maybeWaitOnVibration(vib, commonOptions);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index 3f0226663cff..f48178c5b9f7 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -124,7 +124,7 @@ class WallpaperDisplayHelper {
final long ident = Binder.clearCallingIdentity();
try {
- return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+ return mWindowManagerInternal.isHomeSupportedOnDisplay(displayId);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index bdcde66a8102..f8078d2e9f27 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -869,8 +869,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- int which = mWallpaper.mWhich;
- clearWallpaperLocked(which, mWallpaper.userId, false, null);
+ clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null);
}
}
};
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b3ae2ee30f22..b1abe2a567e8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -558,7 +558,7 @@ final class AccessibilityController {
}
if (newTarget != null) {
int displayId = newTarget.getDisplayId();
- IBinder clientBinder = newTarget.getIWindow().asBinder();
+ IBinder clientBinder = newTarget.getWindowToken();
mFocusedWindow.put(displayId, clientBinder);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index cdd1a2699e5b..3cf19ddbd89d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -34,7 +34,6 @@ import android.os.Message;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.IWindow;
import android.view.InputWindowHandle;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
@@ -197,14 +196,14 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
final HashMap<IBinder, Matrix> windowsTransformMatrixMap = new HashMap<>();
for (InputWindowHandle inputWindowHandle : windows) {
- final IWindow iWindow = inputWindowHandle.getWindow();
- final WindowState windowState = iWindow != null ? mService.mWindowMap.get(
- iWindow.asBinder()) : null;
+ final IBinder iWindow = inputWindowHandle.getWindowToken();
+ final WindowState windowState = iWindow != null ? mService.mWindowMap.get(iWindow)
+ : null;
if (windowState != null && windowState.shouldMagnify()) {
final Matrix transformMatrix = new Matrix();
windowState.getTransformationMatrix(sTempFloats, transformMatrix);
- windowsTransformMatrixMap.put(iWindow.asBinder(), transformMatrix);
+ windowsTransformMatrixMap.put(iWindow, transformMatrix);
}
}
@@ -330,8 +329,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
// the old and new windows at the same index should be the
// same, otherwise something changed.
for (int i = 0; i < windowsCount; i++) {
- final IWindow newWindowToken = newWindows.get(i).getWindow();
- final IWindow oldWindowToken = oldWindows.get(i).getWindow();
+ final IBinder newWindowToken = newWindows.get(i).getWindowToken();
+ final IBinder oldWindowToken = oldWindows.get(i).getWindowToken();
final boolean hasNewWindowToken = newWindowToken != null;
final boolean hasOldWindowToken = oldWindowToken != null;
@@ -342,8 +341,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
// If both old and new windows had window tokens, but those tokens differ,
// then the windows have changed.
- if (hasNewWindowToken && hasOldWindowToken
- && !newWindowToken.asBinder().equals(oldWindowToken.asBinder())) {
+ if (hasNewWindowToken && hasOldWindowToken && !newWindowToken.equals(oldWindowToken)) {
return true;
}
}
@@ -393,9 +391,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
for (int index = inputWindowHandles.size() - 1; index >= 0; index--) {
final Matrix windowTransformMatrix = mTempMatrix2;
final InputWindowHandle windowHandle = inputWindowHandles.get(index);
- final IBinder iBinder =
- windowHandle.getWindow() != null ? windowHandle.getWindow().asBinder() : null;
-
+ final IBinder iBinder = windowHandle.getWindowToken();
if (getWindowTransformMatrix(iBinder, windowTransformMatrix)) {
generateMagnificationSpecInverseMatrix(windowHandle, currentMagnificationSpec,
previousMagnificationSpec, windowTransformMatrix);
@@ -645,7 +641,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
*/
public static class AccessibilityWindow {
// Data
- private IWindow mWindow;
+ private IBinder mWindow;
private int mDisplayId;
@WindowManager.LayoutParams.WindowType
private int mType;
@@ -670,9 +666,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
public static AccessibilityWindow initializeData(WindowManagerService service,
InputWindowHandle inputWindowHandle, Matrix magnificationInverseMatrix,
IBinder pipIBinder, Matrix displayMatrix) {
- final IWindow window = inputWindowHandle.getWindow();
- final WindowState windowState = window != null ? service.mWindowMap.get(
- window.asBinder()) : null;
+ final IBinder window = inputWindowHandle.getWindowToken();
+ final WindowState windowState = window != null ? service.mWindowMap.get(window) : null;
final AccessibilityWindow instance = new AccessibilityWindow();
@@ -680,7 +675,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
instance.mDisplayId = inputWindowHandle.displayId;
instance.mInputConfig = inputWindowHandle.inputConfig;
instance.mType = inputWindowHandle.layoutParamsType;
- instance.mIsPIPMenu = window != null && window.asBinder().equals(pipIBinder);
+ instance.mIsPIPMenu = window != null && window.equals(pipIBinder);
// TODO (b/199357848): gets the private flag of the window from other way.
instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0;
@@ -867,7 +862,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
WindowInfo windowInfo = WindowInfo.obtain();
windowInfo.displayId = window.mDisplayId;
windowInfo.type = window.mType;
- windowInfo.token = window.mWindow != null ? window.mWindow.asBinder() : null;
+ windowInfo.token = window.mWindow;
windowInfo.hasFlagWatchOutsideTouch = (window.mInputConfig
& InputConfig.WATCH_OUTSIDE_TOUCH) != 0;
// Set it to true to be consistent with the legacy implementation.
@@ -878,7 +873,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
@Override
public String toString() {
String windowToken =
- mWindow != null ? mWindow.asBinder().toString() : "(no window token)";
+ mWindow != null ? mWindow.toString() : "(no window token)";
return "A11yWindow=[" + windowToken
+ ", displayId=" + mDisplayId
+ ", inputConfig=0x" + Integer.toHexString(mInputConfig)
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 26f0d34a6261..faccca8981c8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -70,7 +70,6 @@ import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.app.compat.CompatChanges;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.app.servertransaction.PipStateTransactionItem;
import android.compat.annotation.ChangeId;
@@ -1018,9 +1017,8 @@ class ActivityClientController extends IActivityClientController.Stub {
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
- transaction.addCallback(EnterPipRequestedItem.obtain(r.token));
- mService.getLifecycleManager().scheduleTransaction(transaction);
+ mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(),
+ EnterPipRequestedItem.obtain(r.token));
return true;
} catch (Exception e) {
Slog.w(TAG, "Failed to send enter pip requested item: "
@@ -1039,9 +1037,8 @@ class ActivityClientController extends IActivityClientController.Stub {
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
- transaction.addCallback(PipStateTransactionItem.obtain(r.token, pipState));
- mService.getLifecycleManager().scheduleTransaction(transaction);
+ mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(),
+ PipStateTransactionItem.obtain(r.token, pipState));
} catch (Exception e) {
Slog.w(TAG, "Failed to send pip state transaction item: "
+ r.intent.getComponent(), e);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index eeeca1018b0a..081759d563a5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -115,7 +115,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.SurfaceControl.getGlobalTransaction;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -277,7 +276,6 @@ import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.MoveToDisplayItem;
@@ -1449,7 +1447,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
config);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
MoveToDisplayItem.obtain(token, displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1466,7 +1464,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
+ "config: %s", this, config);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
ActivityConfigurationChangeItem.obtain(token, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1487,7 +1485,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STATES, "Sending position change to %s, onTop: %b",
this, onTop);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
TopResumedActivityChangeItem.obtain(token, onTop));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1556,9 +1554,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- // Transition change for the activity moving into a TaskFragment of different bounds.
- return newParent.isOrganizedTaskFragment()
- && !newParent.getBounds().equals(oldParent.getBounds());
+ final boolean isInPip2 = ActivityTaskManagerService.isPip2ExperimentEnabled()
+ && inPinnedWindowingMode();
+ if (!newParent.isOrganizedTaskFragment() && !isInPip2) {
+ // Parent TaskFragment isn't associated with a TF organizer and we are not in PiP2,
+ // so do not allow for initializeChangeTransition() on parent changes
+ return false;
+ }
+ // Transition change for the activity moving into TaskFragment of different bounds.
+ return !newParent.getBounds().equals(oldParent.getBounds());
}
@Override
@@ -2744,7 +2748,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
TransferSplashScreenViewStateItem.obtain(token, parcelable,
windowAnimationLeash));
scheduleTransferSplashScreenTimeout();
@@ -3908,7 +3912,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
DestroyActivityItem.obtain(token, finishing, configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process has crashed, our death
@@ -4819,7 +4823,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
final ArrayList<ResultInfo> list = new ArrayList<>();
list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
ActivityResultItem.obtain(token, list));
return;
} catch (Exception e) {
@@ -4830,22 +4834,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Schedule sending results now for Media Projection setup.
if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
STOPPING, STOPPED)) {
- final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
// Build result to be returned immediately.
- transaction.addCallback(ActivityResultItem.obtain(
- token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data))));
+ final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
+ token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data)));
// 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.
- ActivityLifecycleItem lifecycleItem = getLifecycleItemForCurrentStateForResult();
- if (lifecycleItem != null) {
- transaction.setLifecycleStateRequest(lifecycleItem);
- } else {
- Slog.w(TAG, "Unable to get the lifecycle item for state " + mState
- + " so couldn't immediately send result");
- }
+ final ActivityLifecycleItem lifecycleItem = getLifecycleItemForCurrentStateForResult();
try {
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ if (lifecycleItem != null) {
+ mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+ app.getThread(), activityResultItem, lifecycleItem);
+ } else {
+ Slog.w(TAG, "Unable to get the lifecycle item for state " + mState
+ + " so couldn't immediately send result");
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ app.getThread(), activityResultItem);
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending result to " + this, e);
}
@@ -4925,7 +4930,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Making sure the client state is RESUMED after transaction completed and doing
// so only if activity is currently RESUMED. Otherwise, client may have extra
// life-cycle calls to RESUMED (and PAUSED later).
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
NewIntentItem.obtain(token, ar, mState == RESUMED));
unsent = false;
} catch (RemoteException e) {
@@ -5693,14 +5698,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// can be synchronized with showing the next surface in the transition.
if (!usingShellTransitions && !isVisible() && !delayed
&& !displayContent.mAppTransition.isTransitionSet()) {
- SurfaceControl.openTransaction();
- try {
- forAllWindows(win -> {
- win.mWinAnimator.hide(getGlobalTransaction(), "immediately hidden");
- }, true);
- } finally {
- SurfaceControl.closeTransaction();
- }
+ forAllWindows(win -> {
+ win.mWinAnimator.hide(getPendingTransaction(), "immediately hidden");
+ }, true);
+ scheduleAnimation();
}
}
@@ -6150,7 +6151,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this),
shortComponentName, "userLeaving=false", "make-active");
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */, mAutoEnteringPip));
} catch (Exception e) {
@@ -6163,7 +6164,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
setState(STARTED, "makeActiveIfNeeded");
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
StartActivityItem.obtain(token, takeOptions()));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
@@ -6461,7 +6462,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
EventLogTags.writeWmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
StopActivityItem.obtain(token, configChangeFlags));
mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
@@ -8185,6 +8186,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* aspect ratio.
*/
boolean shouldCreateCompatDisplayInsets() {
+ if (mLetterboxUiController.shouldApplyUserFullscreenOverride()) {
+ // If the user has forced the applications aspect ratio to be fullscreen, don't use size
+ // compatibility mode in any situation. The user has been warned and therefore accepts
+ // the risk of the application misbehaving.
+ return false;
+ }
switch (supportsSizeChanges()) {
case SIZE_CHANGES_SUPPORTED_METADATA:
case SIZE_CHANGES_SUPPORTED_OVERRIDE:
@@ -9895,10 +9902,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
lifecycleItem = PauseActivityItem.obtain(token);
}
- final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
- transaction.addCallback(callbackItem);
- transaction.setLifecycleStateRequest(lifecycleItem);
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+ app.getThread(), callbackItem, lifecycleItem);
startRelaunching();
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
@@ -9990,7 +9995,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The process will be killed until the activity reports stopped with saved state (see
// {@link ActivityTaskManagerService.activityStopped}).
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
StopActivityItem.obtain(token, 0 /* configChanges */));
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown during restart " + this, e);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 009b8e048840..5e0a449ddd63 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -133,6 +133,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.BackgroundActivityStartController.BalCode;
+import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
@@ -1090,14 +1091,14 @@ class ActivityStarter {
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
- @BalCode int balCode = BAL_ALLOW_DEFAULT;
+ final BalVerdict balVerdict;
if (!abort) {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
"shouldAbortBackgroundActivityStart");
BackgroundActivityStartController balController =
mSupervisor.getBackgroundActivityLaunchController();
- BackgroundActivityStartController.BalVerdict balVerdict =
+ balVerdict =
balController.checkBackgroundActivityStart(
callingUid,
callingPid,
@@ -1109,13 +1110,13 @@ class ActivityStarter {
request.forcedBalByPiSender,
intent,
checkedOptions);
- balCode = balVerdict.getCode();
- request.logMessage.append(" (").append(
- BackgroundActivityStartController.balCodeToString(balCode))
- .append(")");
+ request.logMessage.append(" (").append(balVerdict).append(")");
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
+ } else {
+ // Sets ALLOW_BY_DEFAULT as default value as the activity launch will be aborted anyway.
+ balVerdict = BalVerdict.ALLOW_BY_DEFAULT;
}
if (request.allowPendingRemoteAnimationRegistryLookup) {
@@ -1293,13 +1294,13 @@ class ActivityStarter {
WindowProcessController homeProcess = mService.mHomeProcess;
boolean isHomeProcess = homeProcess != null
&& aInfo.applicationInfo.uid == homeProcess.mUid;
- if (balCode != BAL_BLOCK && !isHomeProcess) {
+ if (balVerdict.allows() && !isHomeProcess) {
mService.resumeAppSwitches();
}
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, checkedOptions,
- inTask, inTaskFragment, balCode, intentGrants, realCallingUid);
+ inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid);
if (request.outActivity != null) {
request.outActivity[0] = mLastStartActivityRecord;
@@ -1449,7 +1450,8 @@ class ActivityStarter {
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, ActivityOptions options, Task inTask,
- TaskFragment inTaskFragment, @BalCode int balCode,
+ TaskFragment inTaskFragment,
+ BalVerdict balVerdict,
NeededUriGrants intentGrants, int realCallingUid) {
int result = START_CANCELED;
final Task startedActivityRootTask;
@@ -1468,7 +1470,7 @@ class ActivityStarter {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, options, inTask, inTaskFragment, balCode,
+ startFlags, options, inTask, inTaskFragment, balVerdict,
intentGrants, realCallingUid);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -1615,10 +1617,10 @@ class ActivityStarter {
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, ActivityOptions options, Task inTask,
- TaskFragment inTaskFragment, @BalCode int balCode,
+ TaskFragment inTaskFragment, BalVerdict balVerdict,
NeededUriGrants intentGrants, int realCallingUid) {
setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
- voiceSession, voiceInteractor, balCode, realCallingUid);
+ voiceSession, voiceInteractor, balVerdict.getCode(), realCallingUid);
computeLaunchingTaskFlags();
mIntent.setFlags(mLaunchFlags);
@@ -1696,7 +1698,8 @@ class ActivityStarter {
}
recordTransientLaunchIfNeeded(targetTaskTop);
// Recycle the target task for this launch.
- startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
+ startResult =
+ recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants, balVerdict);
if (startResult != START_SUCCESS) {
return startResult;
}
@@ -1730,6 +1733,7 @@ class ActivityStarter {
recordTransientLaunchIfNeeded(mLastStartActivityRecord);
if (!mAvoidMoveToFront && mDoResume) {
+ logOnlyCreatorAllowsBAL(balVerdict, realCallingUid, newTask);
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming()
&& !dreamStopping) {
@@ -1800,6 +1804,7 @@ class ActivityStarter {
// now update the focused root-task accordingly.
if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()
&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
+ logOnlyCreatorAllowsBAL(balVerdict, realCallingUid, newTask);
mTargetRootTask.moveToFront("startActivityInner");
}
mRootWindowContainer.resumeFocusedTasksTopActivities(
@@ -1817,7 +1822,7 @@ class ActivityStarter {
// Note that mStartActivity and source should be in the same Task at this point.
if (mOptions != null && mOptions.isLaunchIntoPip()
&& sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
- && balCode != BAL_BLOCK) {
+ && balVerdict.allows()) {
mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
sourceRecord, "launch-into-pip");
}
@@ -1828,6 +1833,24 @@ class ActivityStarter {
return START_SUCCESS;
}
+ private void logOnlyCreatorAllowsBAL(BalVerdict balVerdict,
+ int realCallingUid, boolean newTask) {
+ // TODO (b/296478675) eventually, we will prevent such case from happening
+ // and probably also log that a BAL is prevented by android V.
+ if (!newTask && balVerdict.onlyCreatorAllows()) {
+ String realCallingPackage =
+ mService.mContext.getPackageManager().getNameForUid(realCallingUid);
+ if (realCallingPackage == null) {
+ realCallingPackage = "uid=" + realCallingUid;
+ }
+ Slog.wtf(TAG, "A background app is brought to the foreground due to a "
+ + "PendingIntent. However, only the creator of the PendingIntent allows BAL, "
+ + "while the sender does not allow BAL. realCallingPackage: "
+ + realCallingPackage + "; callingPackage: " + mRequest.callingPackage
+ + "; mTargetRootTask:" + mTargetRootTask);
+ }
+ }
+
private void recordTransientLaunchIfNeeded(ActivityRecord r) {
if (r == null || !mTransientLaunch) return;
final TransitionController controller = r.mTransitionController;
@@ -1995,7 +2018,7 @@ class ActivityStarter {
*/
@VisibleForTesting
int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask,
- NeededUriGrants intentGrants) {
+ NeededUriGrants intentGrants, BalVerdict balVerdict) {
// Should not recycle task which is from a different user, just adding the starting
// activity to the task.
if (targetTask.mUserId != mStartActivity.mUserId) {
@@ -2024,7 +2047,7 @@ class ActivityStarter {
mRootWindowContainer.startPowerModeLaunchIfNeeded(false /* forceSend */,
targetTaskTop);
- setTargetRootTaskIfNeeded(targetTaskTop);
+ setTargetRootTaskIfNeeded(targetTaskTop, balVerdict);
// When there is a reused activity and the current result is a trampoline activity,
// set the reused activity as the result.
@@ -2040,6 +2063,7 @@ class ActivityStarter {
if (!mMovedToFront && mDoResume) {
ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask,
targetTaskTop);
+ logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false);
mTargetRootTask.moveToFront("intentActivityFound");
}
resumeTargetRootTaskIfNeeded();
@@ -2068,6 +2092,7 @@ class ActivityStarter {
targetTaskTop.showStartingWindow(true /* taskSwitch */);
} else if (mDoResume) {
// Make sure the root task and its belonging display are moved to topmost.
+ logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false);
mTargetRootTask.moveToFront("intentActivityFound");
}
// We didn't do anything... but it was needed (a.k.a., client don't use that intent!)
@@ -2663,7 +2688,7 @@ class ActivityStarter {
* @param intentActivity Existing matching activity.
* @return {@link ActivityRecord} brought to front.
*/
- private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) {
+ private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity, BalVerdict balVerdict) {
intentActivity.getTaskFragment().clearLastPausedActivity();
Task intentTask = intentActivity.getTask();
// The intent task might be reparented while in getOrCreateRootTask, caches the original
@@ -2730,6 +2755,7 @@ class ActivityStarter {
// task on top there.
// Defer resuming the top activity while moving task to top, since the
// current task-top activity may not be the activity that should be resumed.
+ logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false);
mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, DEFER_RESUME,
"bringingFoundTaskToFront");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3c56a4e5eb04..34c7eee45f34 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -125,6 +125,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_R
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
import android.Manifest;
import android.annotation.IntDef;
@@ -165,6 +166,7 @@ import android.app.assist.ActivityId;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.compat.CompatChanges;
+import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -279,6 +281,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.wm.shell.Flags;
import java.io.BufferedReader;
import java.io.File;
@@ -314,8 +317,6 @@ import java.util.Set;
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
- private static final String ENABLE_PIP2_IMPLEMENTATION =
- "persist.wm.debug.enable_pip2_implementation";
static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -414,6 +415,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mHasCompanionDeviceSetupFeature;
/** The process of the top most activity. */
volatile WindowProcessController mTopApp;
+ /** The process showing UI while the device is dozing. */
+ volatile WindowProcessController mVisibleDozeUiProcess;
/**
* This is the process holding the activity the user last visited that is in a different process
* from the one they are currently in.
@@ -1258,6 +1261,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
true /*validateIncomingUser*/);
}
+ static boolean isSdkSandboxActivity(Context context, Intent intent) {
+ return intent != null
+ && (sandboxActivitySdkBasedContext()
+ ? SdkSandboxActivityAuthority.isSdkSandboxActivity(context, intent)
+ : intent.isSandboxActivity(context));
+ }
+
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
@@ -1268,7 +1278,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
- if (intent != null && intent.isSandboxActivity(mContext)) {
+ if (isSdkSandboxActivity(mContext, intent)) {
SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
SdkSandboxManagerLocal.class);
sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
@@ -7251,6 +7261,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
static boolean isPip2ExperimentEnabled() {
- return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+ return Flags.enablePip2Implementation();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 777b5cd4337b..90eeed288240 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -98,7 +98,6 @@ import android.app.ResultInfo;
import android.app.TaskInfo;
import android.app.WaitResult;
import android.app.servertransaction.ActivityLifecycleItem;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.LaunchActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
@@ -928,15 +927,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// Create activity launch transaction.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(
- proc.getThread());
-
final boolean isTransitionForward = r.isTransitionForward();
final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
-
final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
- clientTransaction.addCallback(LaunchActivityItem.obtain(r.token,
- new Intent(r.intent), System.identityHashCode(r), r.info,
+ final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token,
+ r.intent, System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
@@ -945,7 +940,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
- r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));
+ r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken);
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -955,10 +950,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
} else {
lifecycleItem = PauseActivityItem.obtain(r.token);
}
- clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
- mService.getLifecycleManager().scheduleTransaction(clientTransaction);
+ mService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+ proc.getThread(), launchActivityItem, lifecycleItem);
if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
// If the seq is increased, there should be something changed (e.g. registered
@@ -1089,7 +1084,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Remove the process record so it won't be considered as alive.
mService.mProcessNames.remove(wpc.mName, wpc.mUid);
mService.mProcessMap.remove(wpc.getPid());
- } else if (r.intent.isSandboxActivity(mService.mContext)) {
+ } else if (ActivityTaskManagerService.isSdkSandboxActivity(mService.mContext, r.intent)) {
Slog.e(TAG, "Abort sandbox activity launching as no sandbox process to host it.");
r.finishIfPossible("No sandbox process for the activity", false /* oomAdj */);
r.launchFailed = true;
@@ -1633,7 +1628,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
- private void removeRootTaskInSurfaceTransaction(Task rootTask) {
+ /**
+ * Removes the root task associated with the given {@param rootTask}. If the {@param rootTask}
+ * is the pinned task, then its child tasks are not explicitly removed when the root task is
+ * destroyed, but instead moved back onto the TaskDisplayArea.
+ */
+ void removeRootTask(Task rootTask) {
if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
removePinnedRootTaskInSurfaceTransaction(rootTask);
} else {
@@ -1644,15 +1644,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
/**
- * Removes the root task associated with the given {@param task}. If the {@param task} is the
- * pinned task, then its child tasks are not explicitly removed when the root task is
- * destroyed, but instead moved back onto the TaskDisplayArea.
- */
- void removeRootTask(Task task) {
- mWindowManager.inSurfaceTransaction(() -> removeRootTaskInSurfaceTransaction(task));
- }
-
- /**
* Removes the task with the specified task id.
*
* @param taskId Identifier of the task to be removed.
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 805bff240f66..05087f8a6edf 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -71,7 +71,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -82,7 +81,6 @@ import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
-import android.util.Slog;
import android.view.Display;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
@@ -1179,16 +1177,7 @@ public class AppTransitionController {
}
app.updateReportedVisibilityLocked();
app.waitingToShow = false;
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION handleAppTransitionReady()");
- mService.openSurfaceTransaction();
- try {
- app.showAllWindowsLocked();
- } finally {
- mService.closeSurfaceTransaction("handleAppTransitionReady");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION handleAppTransitionReady()");
- }
+ app.showAllWindowsLocked();
if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
app.attachThumbnailAnimation();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 87ae045d4f12..43f32096b6c0 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -39,7 +39,6 @@ import android.content.res.ResourceId;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -60,7 +59,6 @@ import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.server.LocalServices;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
@@ -151,97 +149,77 @@ class BackNavigationController {
// Don't start any animation for it.
return null;
}
- WindowManagerInternal windowManagerInternal =
- LocalServices.getService(WindowManagerInternal.class);
- IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
window = wmService.getFocusedWindowLocked();
if (window == null) {
- EmbeddedWindowController.EmbeddedWindow embeddedWindow =
- wmService.mEmbeddedWindowController.getByInputTransferToken(
- focusedWindowToken);
- if (embeddedWindow != null) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
- "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.");
- return null;
- }
- }
-
- // Lets first gather the states of things
- // - What is our current window ?
- // - Does it has an Activity and a Task ?
- // TODO Temp workaround for Sysui until b/221071505 is fixed
- if (window != null) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
- "Focused window found using getFocusedWindowToken");
- }
-
- if (window != null) {
- // This is needed to bridge the old and new back behavior with recents. While in
- // Overview with live tile enabled, the previous app is technically focused but we
- // add an input consumer to capture all input that would otherwise go to the apps
- // being controlled by the animation. This means that the window resolved is not
- // the right window to consume back while in overview, so we need to route it to
- // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing
- // compat callback in VRI only works when the window is focused.
- // This symptom also happen while shell transition enabled, we can check that by
- // isTransientLaunch to know whether the focus window is point to live tile.
- final RecentsAnimationController recentsAnimationController =
- wmService.getRecentsAnimationController();
- final ActivityRecord ar = window.mActivityRecord;
- if ((ar != null && ar.isActivityTypeHomeOrRecents()
- && ar.mTransitionController.isTransientLaunch(ar))
- || (recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(ar))) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by "
- + "recents. Overriding back callback to recents controller callback.");
- return null;
- }
-
- if (!window.isDrawn()) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
- "Focused window didn't have a valid surface drawn.");
- return null;
- }
- }
-
- if (window == null) {
// We don't have any focused window, fallback ont the top currentTask of the focused
// display.
ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
"No focused window, defaulting to top current task's window");
currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask();
- window = currentTask.getWindow(WindowState::isFocused);
+ window = currentTask != null
+ ? currentTask.getWindow(WindowState::isFocused) : null;
+ }
+
+ if (window == null) {
+ Slog.e(TAG, "Window is null, returning null.");
+ return null;
}
+ // This is needed to bridge the old and new back behavior with recents. While in
+ // Overview with live tile enabled, the previous app is technically focused but we
+ // add an input consumer to capture all input that would otherwise go to the apps
+ // being controlled by the animation. This means that the window resolved is not
+ // the right window to consume back while in overview, so we need to route it to
+ // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing
+ // compat callback in VRI only works when the window is focused.
+ // This symptom also happen while shell transition enabled, we can check that by
+ // isTransientLaunch to know whether the focus window is point to live tile.
+ final RecentsAnimationController recentsAnimationController =
+ wmService.getRecentsAnimationController();
+ final ActivityRecord tmpAR = window.mActivityRecord;
+ if ((tmpAR != null && tmpAR.isActivityTypeHomeOrRecents()
+ && tmpAR.mTransitionController.isTransientLaunch(tmpAR))
+ || (recentsAnimationController != null
+ && recentsAnimationController.shouldApplyInputConsumer(tmpAR))) {
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by "
+ + "recents. Overriding back callback to recents controller callback.");
+ return null;
+ }
+
+ if (!window.isDrawn()) {
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
+ "Focused window didn't have a valid surface drawn.");
+ return null;
+ }
+
+ currentActivity = window.mActivityRecord;
+ currentTask = window.getTask();
+ if ((currentTask != null && !currentTask.isVisibleRequested())
+ || (currentActivity != null && !currentActivity.isVisibleRequested())) {
+ // Closing transition is happening on focus window and should be update soon,
+ // don't drive back navigation with it.
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Focus window is closing.");
+ return null;
+ }
// Now let's find if this window has a callback from the client side.
- OnBackInvokedCallbackInfo callbackInfo = null;
- if (window != null) {
- currentActivity = window.mActivityRecord;
- currentTask = window.getTask();
- callbackInfo = window.getOnBackInvokedCallbackInfo();
- if (callbackInfo == null) {
- Slog.e(TAG, "No callback registered, returning null.");
- return null;
- }
- if (!callbackInfo.isSystemCallback()) {
- backType = BackNavigationInfo.TYPE_CALLBACK;
- }
- infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
- infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
- mNavigationMonitor.startMonitor(window, navigationObserver);
+ final OnBackInvokedCallbackInfo callbackInfo = window.getOnBackInvokedCallbackInfo();
+ if (callbackInfo == null) {
+ Slog.e(TAG, "No callback registered, returning null.");
+ return null;
}
+ if (!callbackInfo.isSystemCallback()) {
+ backType = BackNavigationInfo.TYPE_CALLBACK;
+ }
+ infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
+ infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
+ mNavigationMonitor.startMonitor(window, navigationObserver);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
+ "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s",
currentTask, currentActivity, callbackInfo, window);
- if (window == null) {
- Slog.e(TAG, "Window is null, returning null.");
- return null;
- }
-
// If we don't need to set up the animation, we return early. This is the case when
// - We have an application callback.
// - We don't have any ActivityRecord or Task to animate.
@@ -322,12 +300,13 @@ class BackNavigationController {
}
return false;
}, currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/);
- final ActivityRecord tmpPre = prevTask.getTopNonFinishingActivity();
+ final ActivityRecord tmpPre = prevTask != null
+ ? prevTask.getTopNonFinishingActivity() : null;
if (tmpPre != null) {
prevActivities.add(tmpPre);
findAdjacentActivityIfExist(tmpPre, prevActivities);
}
- if (prevActivities.isEmpty()
+ if (prevTask == null || prevActivities.isEmpty()
|| (isOccluded && !prevActivities.get(0).canShowWhenLocked())) {
backType = BackNavigationInfo.TYPE_CALLBACK;
} else if (prevTask.isActivityTypeHome()) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 1b5631f59a3e..287aaf9da42a 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
+import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
import static com.android.window.flags.Flags.balShowToasts;
import static com.android.window.flags.Flags.balShowToastsBlocked;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
@@ -42,9 +43,14 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -79,6 +85,12 @@ 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;
+ /** If enabled the creator will not allow BAL on its behalf by default. */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Overridable
+ private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
+ 296478951;
public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -256,13 +268,21 @@ public class BackgroundActivityStartController {
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
+ if (originatingPendingIntent == null) {
+ // grant creator BAL privileges unless explicitly opted out
+ mBalAllowedByPiCreator =
+ checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ } else {
+ // for PendingIntents we restrict BAL based on target_sdk
+ mBalAllowedByPiCreator = getBackgroundStartPrivilegesAllowedByCreator(
+ callingUid, callingPackage, checkedOptions);
+ }
mBalAllowedByPiSender =
- PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(checkedOptions,
- realCallingUid, mRealCallingPackage);
- mBalAllowedByPiCreator =
- checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
- ? BackgroundStartPrivileges.NONE : BackgroundStartPrivileges.ALLOW_BAL;
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid, mRealCallingPackage);
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -292,6 +312,45 @@ public class BackgroundActivityStartController {
}
}
+ private BackgroundStartPrivileges getBackgroundStartPrivilegesAllowedByCreator(
+ int callingUid, String callingPackage, ActivityOptions checkedOptions) {
+ switch (checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()) {
+ case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ return BackgroundStartPrivileges.ALLOW_BAL;
+ case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED:
+ return BackgroundStartPrivileges.NONE;
+ case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
+ // no explicit choice by the app - let us decide what to do
+ Slog.i(TAG, "balRequireOptInByPendingIntentCreator = "
+ + balRequireOptInByPendingIntentCreator());
+ if (!balRequireOptInByPendingIntentCreator()) {
+ // if feature is disabled allow
+ return BackgroundStartPrivileges.ALLOW_BAL;
+ }
+ if (callingPackage != null) {
+ // determine based on the calling/creating package
+ boolean changeEnabled = CompatChanges.isChangeEnabled(
+ DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR,
+ callingPackage,
+ UserHandle.getUserHandleForUid(callingUid));
+ Slog.i(TAG, "changeEnabled = " + changeEnabled);
+ return changeEnabled ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ }
+ // determine based on the calling/creating uid if we cannot determine the
+ // actual package name (e.g. shared uid)
+ boolean changeEnabled = CompatChanges.isChangeEnabled(
+ DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR,
+ callingUid);
+ Slog.i(TAG, "changeEnabled = " + changeEnabled);
+ return changeEnabled ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ default:
+ throw new IllegalStateException("unsupported BackgroundActivityStartMode: "
+ + checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode());
+ }
+ }
+
private String getDebugPackageName(String packageName, int uid) {
if (packageName != null) {
return packageName; // use actual package
@@ -306,10 +365,14 @@ public class BackgroundActivityStartController {
return name + "[debugOnly]";
}
- private boolean isPendingIntent() {
+ private boolean hasRealCaller() {
return mRealCallingUid != NO_PROCESS_UID;
}
+ private boolean isPendingIntent() {
+ return mOriginatingPendingIntent != null && hasRealCaller();
+ }
+
private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
Preconditions.checkState(!isPendingIntent());
return dump(resultIfPiCreatorAllowsBal, null);
@@ -326,16 +389,23 @@ public class BackgroundActivityStartController {
.append(getDebugPackageName(mCallingPackage, mCallingUid));
sb.append("; callingUid: ").append(mCallingUid);
sb.append("; callingPid: ").append(mCallingPid);
- sb.append("; isPendingIntent: ").append(isPendingIntent());
sb.append("; appSwitchState: ").append(mAppSwitchState);
sb.append("; callingUidHasAnyVisibleWindow: ").append(mCallingUidHasAnyVisibleWindow);
sb.append("; callingUidProcState: ").append(DebugUtils.valueToString(
ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState));
sb.append("; isCallingUidPersistentSystemProcess: ")
.append(mIsCallingUidPersistentSystemProcess);
+ sb.append("; forcedBalByPiSender: ").append(mForcedBalByPiSender);
+ sb.append("; intent: ").append(mIntent);
+ sb.append("; callerApp: ").append(mCallerApp);
+ if (mCallerApp != null) {
+ sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
+ }
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
- if (isPendingIntent()) {
- sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
+ sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
+ sb.append("; hasRealCaller: ").append(hasRealCaller());
+ sb.append("; isPendingIntent: ").append(isPendingIntent());
+ if (hasRealCaller()) {
sb.append("; realCallingPackage: ")
.append(getDebugPackageName(mRealCallingPackage, mRealCallingUid));
sb.append("; realCallingUid: ").append(mRealCallingUid);
@@ -347,24 +417,14 @@ public class BackgroundActivityStartController {
sb.append("; isRealCallingUidPersistentSystemProcess: ")
.append(mIsRealCallingUidPersistentSystemProcess);
sb.append("; originatingPendingIntent: ").append(mOriginatingPendingIntent);
- }
- sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender);
- sb.append("; intent: ").append(mIntent);
- sb.append("; callerApp: ").append(mCallerApp);
- if (isPendingIntent()) {
sb.append("; realCallerApp: ").append(mRealCallerApp);
- }
- if (mCallerApp != null) {
- sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
- }
- if (isPendingIntent()) {
if (mRealCallerApp != null) {
sb.append("; realInVisibleTask: ")
.append(mRealCallerApp.hasActivityInVisibleTask());
}
+ sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
sb.append("; resultIfPiSenderAllowsBal: ").append(resultIfPiSenderAllowsBal);
}
- sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
sb.append("]");
return sb.toString();
}
@@ -373,10 +433,15 @@ public class BackgroundActivityStartController {
static class BalVerdict {
static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked");
+ static final BalVerdict ALLOW_BY_DEFAULT =
+ new BalVerdict(BAL_ALLOW_DEFAULT, false, "Default");
private final @BalCode int mCode;
private final boolean mBackground;
private final String mMessage;
private String mProcessInfo;
+ // indicates BAL would be blocked because only creator of the PI has the privilege to allow
+ // BAL, the sender does not have the privilege to allow BAL.
+ private boolean mOnlyCreatorAllows;
BalVerdict(@BalCode int balCode, boolean background, String message) {
this.mBackground = background;
@@ -397,12 +462,20 @@ public class BackgroundActivityStartController {
return !blocks();
}
+ BalVerdict setOnlyCreatorAllows(boolean onlyCreatorAllows) {
+ mOnlyCreatorAllows = onlyCreatorAllows;
+ return this;
+ }
+
+ boolean onlyCreatorAllows() {
+ return mOnlyCreatorAllows;
+ }
+
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append(". BAL Code: ");
builder.append(balCodeToString(mCode));
if (DEBUG_ACTIVITY_STARTS) {
- builder.append(" ");
+ builder.append(" (");
if (mBackground) {
builder.append("Background ");
}
@@ -416,6 +489,7 @@ public class BackgroundActivityStartController {
builder.append(" ");
builder.append(mProcessInfo);
}
+ builder.append(")");
}
return builder.toString();
}
@@ -484,19 +558,16 @@ public class BackgroundActivityStartController {
BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
- if (!state.isPendingIntent()) {
+ if (!state.hasRealCaller()) {
+ BalVerdict resultForRealCaller = null; // nothing to compute
if (resultForCaller.allows()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed. "
- + state.dump(resultForCaller));
+ + state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForCaller, state);
}
- // anything that has fallen through would currently be aborted
- Slog.w(TAG, "Background activity launch blocked! "
- + state.dump(resultForCaller));
- showBalBlockedToast("BAL blocked", state);
- return statsLog(BalVerdict.BLOCK, state);
+ return abortLaunch(state, resultForCaller, resultForRealCaller);
}
// The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -509,6 +580,10 @@ public class BackgroundActivityStartController {
BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
? resultForCaller
: checkBackgroundActivityStartAllowedBySender(state, checkedOptions);
+ if (state.isPendingIntent()) {
+ resultForCaller.setOnlyCreatorAllows(
+ resultForCaller.allows() && resultForRealCaller.blocks());
+ }
if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
@@ -528,37 +603,48 @@ public class BackgroundActivityStartController {
}
return statsLog(resultForRealCaller, state);
}
- if (resultForCaller.allows() && resultForRealCaller.allows()
+ boolean callerCanAllow = resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+ boolean realCallerCanAllow = resultForRealCaller.allows()
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+ if (callerCanAllow && realCallerCanAllow) {
// Both caller and real caller allow with system defined behavior
+ 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+"
+ + " AND the PI sender upgrades target_sdk to 34+! "
+ + state.dump(resultForCaller, resultForRealCaller));
+ showBalRiskToast("BAL would be blocked", state);
+ // return the realCaller result for backwards compatibility
+ return statsLog(resultForRealCaller, state);
+ }
Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start may be blocked"
- + " if the PI creator upgrades target_sdk to 35+"
- + " AND the PI sender upgrades target_sdk to 34+! "
- + " (missing opt in by PI creator)! "
+ "Without Android 15 BAL hardening this activity start would be allowed"
+ + " (missing opt in by PI creator or sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
- // return the realCaller result for backwards compatibility
- return statsLog(resultForRealCaller, state);
+ return abortLaunch(state, resultForCaller, resultForRealCaller);
}
- if (resultForCaller.allows()
- && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ if (callerCanAllow) {
// 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(resultForCaller, resultForRealCaller));
+ showBalRiskToast("BAL would be blocked", state);
+ return statsLog(resultForCaller, state);
+ }
Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start may be blocked"
- + " if the PI creator upgrades target_sdk to 35+! "
+ "Without Android 15 BAL hardening this activity start would be allowed"
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
- return statsLog(resultForCaller, state);
+ return abortLaunch(state, resultForCaller, resultForRealCaller);
}
- if (resultForRealCaller.allows()
- && checkedOptions.getPendingIntentBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ if (realCallerCanAllow) {
// Allowed before U by sender
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG,
@@ -566,15 +652,20 @@ public class BackgroundActivityStartController {
+ " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalBlockedToast("BAL would be blocked", state);
+ showBalRiskToast("BAL would be blocked", state);
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- // fall through
+ return abortLaunch(state, resultForCaller, resultForRealCaller);
}
- // anything that has fallen through would currently be aborted
+ // neither the caller not the realCaller can allow or have explicitly opted out
+ return abortLaunch(state, resultForCaller, resultForRealCaller);
+ }
+
+ private BalVerdict abortLaunch(BalState state, BalVerdict resultForCaller,
+ BalVerdict resultForRealCaller) {
Slog.w(TAG, "Background activity launch blocked! "
+ state.dump(resultForCaller, resultForRealCaller));
showBalBlockedToast("BAL blocked", state);
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index ef31837f810b..28f656e624fb 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -40,35 +40,51 @@ class ClientLifecycleManager {
* @throws RemoteException
*
* @see ClientTransaction
+ * @deprecated use {@link #scheduleTransactionItem(IApplicationThread, ClientTransactionItem)}.
*/
+ @Deprecated
void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
- transaction.schedule();
- if (!(client instanceof Binder)) {
- // If client is not an instance of Binder - it's a remote call and at this point it is
- // safe to recycle the object. All objects used for local calls will be recycled after
- // the transaction is executed on client in ActivityThread.
- transaction.recycle();
+ try {
+ transaction.schedule();
+ } finally {
+ if (!(client instanceof Binder)) {
+ // If client is not an instance of Binder - it's a remote call and at this point it
+ // is safe to recycle the object. All objects used for local calls will be recycled
+ // after the transaction is executed on client in ActivityThread.
+ transaction.recycle();
+ }
}
}
/**
* Schedules a single transaction item, either a callback or a lifecycle request, delivery to
* client application.
- * @param client Target client.
- * @param transactionItem A transaction item to deliver a message.
* @throws RemoteException
- *
* @see ClientTransactionItem
*/
- void scheduleTransaction(@NonNull IApplicationThread client,
+ void scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- if (transactionItem instanceof ActivityLifecycleItem) {
+ if (transactionItem.isActivityLifecycleItem()) {
clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
} else {
clientTransaction.addCallback(transactionItem);
}
scheduleTransaction(clientTransaction);
}
+
+ /**
+ * Schedules a single transaction item with a lifecycle request, delivery to client application.
+ * @throws RemoteException
+ * @see ClientTransactionItem
+ */
+ void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
+ @NonNull ClientTransactionItem transactionItem,
+ @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException {
+ final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
+ clientTransaction.addCallback(transactionItem);
+ clientTransaction.setLifecycleStateRequest(lifecycleItem);
+ scheduleTransaction(clientTransaction);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7f80807e137b..2c224e458a2d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -184,6 +184,7 @@ import android.graphics.Region;
import android.graphics.Region.Op;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Debug;
@@ -1003,6 +1004,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTmpApplySurfaceChangesTransactionState.obscured;
final RootWindowContainer root = mWmService.mRoot;
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ final boolean committed = w.mWinAnimator.commitFinishDrawingLocked();
+ if (isDefaultDisplay && committed) {
+ if (w.hasWallpaper()) {
+ ProtoLog.v(WM_DEBUG_WALLPAPER,
+ "First draw done in potential wallpaper target %s", w);
+ mWallpaperMayChange = true;
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ surfacePlacer.debugLayoutRepeats(
+ "wallpaper and commitFinishDrawingLocked true",
+ pendingLayoutChanges);
+ }
+ }
+ }
+ }
+
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -1089,30 +1108,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
w.handleWindowMovedIfNeeded();
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
w.resetContentChanged();
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- final boolean committed = winAnimator.commitFinishDrawingLocked();
- if (isDefaultDisplay && committed) {
- if (w.hasWallpaper()) {
- ProtoLog.v(WM_DEBUG_WALLPAPER,
- "First draw done in potential wallpaper target %s", w);
- mWallpaperMayChange = true;
- pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) {
- surfacePlacer.debugLayoutRepeats(
- "wallpaper and commitFinishDrawingLocked true",
- pendingLayoutChanges);
- }
- }
- }
- }
-
final ActivityRecord activity = w.mActivityRecord;
if (activity != null && activity.isVisibleRequested()) {
activity.updateLetterboxSurface(w);
@@ -2191,7 +2189,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* @see DisplayWindowPolicyController#getCustomHomeComponent() ()
*/
@Nullable ComponentName getCustomHomeComponent() {
- if (!supportsSystemDecorations() || mDwpcHelper == null) {
+ if (!isHomeSupported() || mDwpcHelper == null) {
return null;
}
return mDwpcHelper.getCustomHomeComponent();
@@ -5586,12 +5584,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
- final Transaction transaction = getPendingTransaction();
super.prepareSurfaces();
-
- // TODO: Once we totally eliminate global transaction we will pass transaction in here
- // rather than merging to global.
- SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -5772,6 +5765,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
&& isTrusted();
}
+ /**
+ * Checks if this display is configured and allowed to show home activity and wallpaper.
+ *
+ * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+ * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+ */
+ boolean isHomeSupported() {
+ return (mWmService.mDisplayWindowSettings.isHomeSupportedLocked(this) && isTrusted())
+ || supportsSystemDecorations();
+ }
+
SurfaceControl getWindowingLayer() {
return mWindowingLayer;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8a7cc67c3660..708ee7f59726 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -99,6 +99,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.PrintWriterPrinter;
@@ -781,6 +782,12 @@ public class DisplayPolicy {
if (!mDisplayContent.isDefaultDisplay) {
return;
}
+ if (awake) {
+ mService.mAtmService.mVisibleDozeUiProcess = null;
+ } else if (mScreenOnFully && mNotificationShade != null) {
+ // Screen is still on, so it may be showing an always-on UI.
+ mService.mAtmService.mVisibleDozeUiProcess = mNotificationShade.getProcess();
+ }
mService.mAtmService.mKeyguardController.updateDeferTransitionForAod(
mAwake /* waiting */);
}
@@ -826,12 +833,24 @@ public class DisplayPolicy {
}
public void screenTurnedOn(ScreenOnListener screenOnListener) {
+ WindowProcessController visibleDozeUiProcess = null;
synchronized (mLock) {
mScreenOnEarly = true;
mScreenOnFully = false;
mKeyguardDrawComplete = false;
mWindowManagerDrawComplete = false;
mScreenOnListener = screenOnListener;
+ if (!mAwake && mNotificationShade != null) {
+ // The screen is turned on without awake state. It is usually triggered by an
+ // adding notification, so make the UI process have a higher priority.
+ visibleDozeUiProcess = mNotificationShade.getProcess();
+ mService.mAtmService.mVisibleDozeUiProcess = visibleDozeUiProcess;
+ }
+ }
+ // The method calls AM directly, so invoke it outside the lock.
+ if (visibleDozeUiProcess != null) {
+ Trace.instant(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurnedOnWhileDozing");
+ mService.mAtmService.setProcessAnimatingWhileDozing(visibleDozeUiProcess);
}
}
@@ -842,6 +861,7 @@ public class DisplayPolicy {
mKeyguardDrawComplete = false;
mWindowManagerDrawComplete = false;
mScreenOnListener = null;
+ mService.mAtmService.mVisibleDozeUiProcess = null;
}
}
@@ -1877,15 +1897,12 @@ public class DisplayPolicy {
final InsetsState insetsState = df.mInsetsState;
final Rect displayFrame = insetsState.getDisplayFrame();
final Insets decor = insetsState.calculateInsets(displayFrame,
- dc.mWmService.mDecorTypes,
- true /* ignoreVisibility */);
- final Insets statusBar = insetsState.calculateInsets(displayFrame,
- Type.statusBars(), true /* ignoreVisibility */);
+ dc.mWmService.mDecorTypes, true /* ignoreVisibility */);
+ final Insets configInsets = insetsState.calculateInsets(displayFrame,
+ dc.mWmService.mConfigTypes, true /* ignoreVisibility */);
mNonDecorInsets.set(decor.left, decor.top, decor.right, decor.bottom);
- mConfigInsets.set(Math.max(statusBar.left, decor.left),
- Math.max(statusBar.top, decor.top),
- Math.max(statusBar.right, decor.right),
- Math.max(statusBar.bottom, decor.bottom));
+ mConfigInsets.set(configInsets.left, configInsets.top, configInsets.right,
+ configInsets.bottom);
mNonDecorFrame.set(displayFrame);
mNonDecorFrame.inset(mNonDecorInsets);
mConfigFrame.set(displayFrame);
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 534cdc2015e3..e808decc354d 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -38,7 +38,6 @@ import static com.android.server.wm.DisplayRotationReversionController.REVERSION
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.RefreshCallbackItem;
import android.app.servertransaction.ResumeActivityItem;
import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -226,13 +225,12 @@ final class DisplayRotationCompatPolicy {
ProtoLog.v(WM_DEBUG_STATES,
"Refreshing activity for camera compatibility treatment, "
+ "activityRecord=%s", activity);
- final ClientTransaction transaction = ClientTransaction.obtain(
- activity.app.getThread());
- transaction.addCallback(RefreshCallbackItem.obtain(activity.token,
- cycleThroughStop ? ON_STOP : ON_PAUSE));
- transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(activity.token,
- /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
- activity.mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
+ activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+ final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
+ activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+ activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+ activity.app.getThread(), refreshCallbackItem, resumeActivityItem);
mHandler.postDelayed(
() -> onActivityRefreshed(activity),
REFRESH_CALLBACK_TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index e1753d7d6257..7a95c2d6d934 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -237,6 +237,37 @@ class DisplayWindowSettings {
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ boolean isHomeSupportedLocked(@NonNull DisplayContent dc) {
+ if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ // Default display should show home.
+ return true;
+ }
+
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+ return settings.mIsHomeSupported != null
+ ? settings.mIsHomeSupported
+ : shouldShowSystemDecorsLocked(dc);
+ }
+
+ void setHomeSupportedOnDisplayLocked(@NonNull String displayUniqueId, int displayType,
+ boolean supported) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = displayUniqueId;
+ displayInfo.type = displayType;
+ final SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getOverrideSettings(displayInfo);
+ overrideSettings.mIsHomeSupported = supported;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
+ void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = displayUniqueId;
+ displayInfo.type = displayType;
+ mSettingsProvider.clearDisplaySettings(displayInfo);
+ }
+
@DisplayImePolicy
int getImePolicyLocked(@NonNull DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
@@ -382,11 +413,18 @@ class DisplayWindowSettings {
void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides);
/**
- * Called when a display is removed to cleanup.
+ * Called when a display is removed to cleanup. Note that for non-virtual displays the
+ * relevant settings entry will be kept, if non-empty.
*/
void onDisplayRemoved(@NonNull DisplayInfo info);
/**
+ * Explicitly removes all settings entory for the given {@link DisplayInfo}, even if it is
+ * not empty.
+ */
+ void clearDisplaySettings(@NonNull DisplayInfo info);
+
+ /**
* Settings for a display.
*/
class SettingsEntry {
@@ -411,6 +449,8 @@ class DisplayWindowSettings {
@Nullable
Boolean mShouldShowSystemDecors;
@Nullable
+ Boolean mIsHomeSupported;
+ @Nullable
Integer mImePolicy;
@Nullable
Integer mFixedToUserRotation;
@@ -479,6 +519,10 @@ class DisplayWindowSettings {
mShouldShowSystemDecors = other.mShouldShowSystemDecors;
changed = true;
}
+ if (!Objects.equals(other.mIsHomeSupported, mIsHomeSupported)) {
+ mIsHomeSupported = other.mIsHomeSupported;
+ changed = true;
+ }
if (!Objects.equals(other.mImePolicy, mImePolicy)) {
mImePolicy = other.mImePolicy;
changed = true;
@@ -561,6 +605,11 @@ class DisplayWindowSettings {
mShouldShowSystemDecors = delta.mShouldShowSystemDecors;
changed = true;
}
+ if (delta.mIsHomeSupported != null && !Objects.equals(
+ delta.mIsHomeSupported, mIsHomeSupported)) {
+ mIsHomeSupported = delta.mIsHomeSupported;
+ changed = true;
+ }
if (delta.mImePolicy != null
&& !Objects.equals(delta.mImePolicy, mImePolicy)) {
mImePolicy = delta.mImePolicy;
@@ -599,6 +648,7 @@ class DisplayWindowSettings {
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& mShouldShowWithInsecureKeyguard == null
&& mShouldShowSystemDecors == null
+ && mIsHomeSupported == null
&& mImePolicy == null
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
@@ -622,6 +672,7 @@ class DisplayWindowSettings {
&& Objects.equals(mShouldShowWithInsecureKeyguard,
that.mShouldShowWithInsecureKeyguard)
&& Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors)
+ && Objects.equals(mIsHomeSupported, that.mIsHomeSupported)
&& Objects.equals(mImePolicy, that.mImePolicy)
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
@@ -633,9 +684,9 @@ class DisplayWindowSettings {
public int hashCode() {
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
- mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
- mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
- mDontMoveToTop);
+ mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
+ mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
+ mIgnoreDisplayCutout, mDontMoveToTop);
}
@Override
@@ -651,6 +702,7 @@ class DisplayWindowSettings {
+ ", mRemoveContentMode=" + mRemoveContentMode
+ ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard
+ ", mShouldShowSystemDecors=" + mShouldShowSystemDecors
+ + ", mIsHomeSupported=" + mIsHomeSupported
+ ", mShouldShowIme=" + mImePolicy
+ ", mFixedToUserRotation=" + mFixedToUserRotation
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index ea668faddc37..c79565ae79fa 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -164,6 +164,11 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
mOverrideSettings.onDisplayRemoved(info);
}
+ @Override
+ public void clearDisplaySettings(@NonNull DisplayInfo info) {
+ mOverrideSettings.clearDisplaySettings(info);
+ }
+
@VisibleForTesting
int getOverrideSettingsSize() {
return mOverrideSettings.mSettings.size();
@@ -291,6 +296,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
}
}
+ void clearDisplaySettings(@NonNull DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ mSettings.remove(identifier);
+ mVirtualDisplayIdentifiers.remove(identifier);
+ }
+
private void writeSettings() {
final FileData fileData = new FileData();
fileData.mIdentifierType = mIdentifierType;
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 14628782eac7..b7eab085f5e3 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -30,7 +30,6 @@ import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
@@ -71,7 +70,7 @@ class EmbeddedWindowController {
mWindowsByInputTransferToken.put(inputTransferToken, window);
mWindowsByWindowToken.put(window.getWindowToken(), window);
updateProcessController(window);
- window.mClient.asBinder().linkToDeath(()-> {
+ window.mClient.linkToDeath(()-> {
synchronized (mGlobalLock) {
mWindows.remove(inputToken);
mWindowsByInputTransferToken.remove(inputTransferToken);
@@ -100,10 +99,10 @@ class EmbeddedWindowController {
}
}
- void remove(IWindow client) {
+ void remove(IBinder client) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
EmbeddedWindow ew = mWindows.valueAt(i);
- if (ew.mClient.asBinder() == client.asBinder()) {
+ if (ew.mClient == client) {
mWindows.removeAt(i).onRemoved();
mWindowsByInputTransferToken.remove(ew.getInputTransferToken());
mWindowsByWindowToken.remove(ew.getWindowToken());
@@ -136,7 +135,7 @@ class EmbeddedWindowController {
}
static class EmbeddedWindow implements InputTarget {
- final IWindow mClient;
+ final IBinder mClient;
@Nullable final WindowState mHostWindowState;
@Nullable final ActivityRecord mHostActivityRecord;
final String mName;
@@ -169,7 +168,7 @@ class EmbeddedWindowController {
* @param windowType to forward to input
* @param displayId used for focus requests
*/
- EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
+ EmbeddedWindow(Session session, WindowManagerService service, IBinder clientToken,
WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
int displayId, IBinder inputTransferToken, String inputHandleName,
boolean isFocusable) {
@@ -241,13 +240,8 @@ class EmbeddedWindowController {
return mWmService.mRoot.getDisplayContent(getDisplayId());
}
- @Override
- public IWindow getIWindow() {
- return mClient;
- }
-
public IBinder getWindowToken() {
- return mClient.asBinder();
+ return mClient;
}
@Override
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 61fea4d9212d..14912d041127 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -57,7 +57,6 @@ import android.os.InputConfig;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import android.view.InputChannel;
@@ -74,7 +73,6 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Set;
import java.util.function.Consumer;
final class InputMonitor {
@@ -258,7 +256,7 @@ final class InputMonitor {
inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
- inputWindowHandle.setWindowToken(w.mClient);
+ inputWindowHandle.setWindowToken(w.mClient.asBinder());
inputWindowHandle.setName(w.getName());
@@ -395,8 +393,12 @@ final class InputMonitor {
*/
void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
final boolean clear = activity == null;
+ final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null;
mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
+ if (clear && wasActive) {
+ setUpdateInputWindowsNeededLw();
+ }
}
private static <T> T getWeak(WeakReference<T> ref) {
@@ -437,8 +439,10 @@ final class InputMonitor {
final InputMethodManagerInternal inputMethodManagerInternal =
LocalServices.getService(InputMethodManagerInternal.class);
if (inputMethodManagerInternal != null) {
- inputMethodManagerInternal.hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
+ // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
+ inputMethodManagerInternal.hideAllInputMethods(
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
+ mDisplayContent.getDisplayId());
}
// Ensure removing the IME snapshot when the app no longer to show on the
// task snapshot (also taking the new task snaphot to update the overview).
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index 653f5f5a74e9..baf0db2e0b7e 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
+import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindow;
/**
* Common interface between focusable objects.
@@ -33,7 +33,7 @@ interface InputTarget {
int getDisplayId();
/* Client IWindow for the target. */
- IWindow getIWindow();
+ IBinder getWindowToken();
/* Owning pid of the target. */
int getPid();
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 90d81bd82087..b74805313d11 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
import android.os.InputConfig;
-import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
import android.view.InputWindowHandle.InputConfigFlags;
@@ -264,8 +263,8 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setWindowToken(IWindow windowToken) {
- if (mHandle.getWindow() == windowToken) {
+ void setWindowToken(IBinder windowToken) {
+ if (mHandle.getWindowToken() == windowToken) {
return;
}
mHandle.setWindowToken(windowToken);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 735cbc4e4287..5518de7b64fd 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -254,7 +254,9 @@ final class LetterboxUiController {
// Counter for ActivityRecord#setRequestedOrientation
private int mSetOrientationRequestCounter = 0;
- // The min aspect ratio override set by user
+ // The min aspect ratio override set by user. Stores the last selected aspect ratio after
+ // {@link #shouldApplyUserFullscreenOverride} or {@link #shouldApplyUserMinAspectRatioOverride}
+ // have been invoked.
@PackageManager.UserMinAspectRatio
private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
@@ -661,7 +663,9 @@ final class LetterboxUiController {
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
- if (shouldApplyUserFullscreenOverride()) {
+ if (shouldApplyUserFullscreenOverride()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -1171,9 +1175,7 @@ final class LetterboxUiController {
boolean shouldApplyUserFullscreenOverride() {
if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
|| FALSE.equals(mBooleanPropertyAllowUserAspectRatioFullscreenOverride)
- || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()
- || mActivityRecord.mDisplayContent == null
- || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b738c1c4bd40..5269d35529bd 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -307,7 +307,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
mService.stopAppSwitches();
}
- mWindowManager.inSurfaceTransaction(() -> {
+ inSurfaceTransaction(() -> {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
mService.deferWindowLayout();
@@ -419,6 +419,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
}
}
+ // No-op wrapper to keep legacy code.
+ private static void inSurfaceTransaction(Runnable exec) {
+ exec.run();
+ }
+
/** Gives the owner of recents animation higher priority. */
private void setProcessAnimating(boolean animating) {
if (mCaller == null) return;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index eb639b6f2033..a98b9f7cd1d1 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -307,7 +307,6 @@ class RemoteAnimationController implements DeathRecipient {
mIsFinishing = true;
unlinkToDeathOfRunner();
releaseFinishedCallback();
- mService.openSurfaceTransaction();
try {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"onAnimationFinished(): Notify animation finished:");
@@ -348,7 +347,6 @@ class RemoteAnimationController implements DeathRecipient {
Slog.e(TAG, "Failed to finish remote animation", e);
throw e;
} finally {
- mService.closeSurfaceTransaction("RemoteAnimationController#finished");
mIsFinishing = false;
}
// Reset input for all activities when the remote animation is finished.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5227a52545f4..0c235bae2006 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -75,7 +75,6 @@ import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
@@ -788,23 +787,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG,
- ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
- }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
- mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG,
- "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
- }
}
// Send any pending task-info changes that were queued-up during a layout deferment
@@ -998,9 +987,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
mWmService.mDisplayManagerInternal.performTraversal(t);
- if (t != defaultDc.mSyncTransaction) {
- SurfaceControl.mergeToGlobalTransaction(t);
- }
}
/**
@@ -1696,8 +1682,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
final DisplayContent display = taskDisplayArea.getDisplayContent();
- if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
- // Can't launch home on display that doesn't support system decorations.
+ if (display == null || display.isRemoved() || !display.isHomeSupported()) {
+ // Can't launch home on display that doesn't support home.
return false;
}
@@ -3126,10 +3112,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (preferredFocusableRootTask != null) {
return preferredFocusableRootTask;
}
- if (preferredDisplayArea.mDisplayContent.supportsSystemDecorations()) {
+
+ if (preferredDisplayArea.mDisplayContent.isHomeSupported()) {
// Stop looking for focusable root task on other displays because the preferred display
- // supports system decorations. Home activity would be launched on the same display if
- // no focusable root task found.
+ // supports home. Home activity would be launched on the same display if no focusable
+ // root task found.
return null;
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index bbb85636f1ee..5c84cb07e891 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -32,6 +32,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATI
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.utils.CoordinateTransforms.computeRotationMatrix;
+import static com.android.window.flags.Flags.removeCaptureDisplay;
import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -170,7 +171,7 @@ class ScreenRotationAnimation {
try {
final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
- if (isSizeChanged) {
+ if (isSizeChanged && !removeCaptureDisplay()) {
final DisplayAddress address = displayInfo.address;
if (!(address instanceof DisplayAddress.Physical)) {
Slog.e(TAG, "Display does not have a physical address: " + displayId);
@@ -196,6 +197,19 @@ class ScreenRotationAnimation {
.setHintForSeamlessTransition(true)
.build();
screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
+ } else if (isSizeChanged) {
+ // Temporarily not skip screenshot for the rounded corner overlays and screenshot
+ // the whole display to include the rounded corner overlays.
+ setSkipScreenshotForRoundedCornerOverlays(false, t);
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(
+ displayContent.getSurfaceControl())
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .setSourceCrop(new Rect(0, 0, width, height))
+ .setHintForSeamlessTransition(true)
+ .build();
+ screenshotBuffer = ScreenCapture.captureLayers(captureArgs);
} else {
ScreenCapture.LayerCaptureArgs captureArgs =
new ScreenCapture.LayerCaptureArgs.Builder(
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0c55d8a85df1..56f9aa4c6361 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -253,8 +253,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void remove(IWindow window) {
- mService.removeWindow(this, window);
+ public void remove(IBinder clientToken) {
+ mService.removeClientToken(this, clientToken);
}
@Override
@@ -894,7 +894,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
+ IBinder clientToken, IBinder hostInputToken, int flags, int privateFlags, int type,
int inputFeatures, IBinder windowToken, IBinder inputTransferToken,
String inputHandleName, InputChannel outInputChannel) {
if (hostInputToken == null && !mCanAddInternalSystemWindow) {
@@ -905,8 +905,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final long identity = Binder.clearCallingIdentity();
try {
- mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
- flags, mCanAddInternalSystemWindow ? privateFlags : 0,
+ mService.grantInputChannel(this, mUid, mPid, displayId, surface, clientToken,
+ hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0,
type, inputFeatures, windowToken, inputTransferToken, inputHandleName,
outInputChannel);
} finally {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a7784152a883..73755121daf8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3513,7 +3513,8 @@ class Task extends TaskFragment {
}
appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton = top != null
&& !appCompatTaskInfo.topActivityInSizeCompat
- && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings()
+ && !info.isTopActivityTransparent;
appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
@@ -4544,12 +4545,6 @@ class Task extends TaskFragment {
* @param creating {@code true} if this is being run during task construction.
*/
void setWindowingMode(int preferredWindowingMode, boolean creating) {
- mWmService.inSurfaceTransaction(() -> setWindowingModeInSurfaceTransaction(
- preferredWindowingMode, creating));
- }
-
- private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode,
- boolean creating) {
final TaskDisplayArea taskDisplayArea = getDisplayArea();
if (taskDisplayArea == null) {
Slog.d(TAG, "taskDisplayArea is null, bail early");
@@ -4624,7 +4619,12 @@ class Task extends TaskFragment {
if (topActivity != null) {
mTaskSupervisor.mNoAnimActivities.add(topActivity);
}
- super.setWindowingMode(windowingMode);
+
+ final boolean isPip2ExperimentEnabled =
+ ActivityTaskManagerService.isPip2ExperimentEnabled();
+ if (!isPip2ExperimentEnabled) {
+ super.setWindowingMode(windowingMode);
+ }
if (currentMode == WINDOWING_MODE_PINNED && topActivity != null) {
// Try reparent pinned activity back to its original task after
@@ -4633,32 +4633,37 @@ class Task extends TaskFragment {
// PiP, we set final windowing mode on the ActivityRecord first and then on its
// Task when the exit PiP transition finishes. Meanwhile, the exit transition is
// always performed on its original task, reparent immediately in ActivityRecord
- // breaks it.
- if (topActivity.getLastParentBeforePip() != null) {
- // Do not reparent if the pinned task is in removal, indicated by the
- // force hidden flag.
- if (!isForceHidden()) {
- final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
- if (lastParentBeforePip.isAttached()) {
- topActivity.reparent(lastParentBeforePip,
- lastParentBeforePip.getChildCount() /* top */,
- "movePinnedActivityToOriginalTask");
- final DisplayContent dc = topActivity.getDisplayContent();
- if (dc != null && dc.isFixedRotationLaunchingApp(topActivity)) {
- // Expanding pip into new rotation, so create a rotation leash
- // until the display is rotated.
- topActivity.getOrCreateFixedRotationLeash(
- topActivity.getSyncTransaction());
- }
- lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
- }
+ // breaks it. Do not reparent if the pinned task is in removal, indicated by the
+ // force hidden flag.
+ if (topActivity.getLastParentBeforePip() != null && !isForceHidden()
+ && topActivity.getLastParentBeforePip().isAttached()) {
+ // We need to collect the pip activity to allow for screenshots
+ // to be taken as a part of reparenting.
+ mTransitionController.collect(topActivity);
+
+ final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
+ topActivity.reparent(lastParentBeforePip,
+ lastParentBeforePip.getChildCount() /* top */,
+ "movePinnedActivityToOriginalTask");
+ final DisplayContent dc = topActivity.getDisplayContent();
+ if (dc != null && dc.isFixedRotationLaunchingApp(topActivity)) {
+ // Expanding pip into new rotation, so create a rotation leash
+ // until the display is rotated.
+ topActivity.getOrCreateFixedRotationLeash(
+ topActivity.getSyncTransaction());
}
+ lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
+ }
+ if (isPip2ExperimentEnabled) {
+ super.setWindowingMode(windowingMode);
}
// Resume app-switches-allowed flag when exiting from pinned mode since
// it does not follow the ActivityStarter path.
if (topActivity.shouldBeVisible()) {
mAtmService.resumeAppSwitches();
}
+ } else if (isPip2ExperimentEnabled) {
+ super.setWindowingMode(windowingMode);
}
if (creating) {
@@ -5965,18 +5970,16 @@ class Task extends TaskFragment {
"Can't exit pinned mode if it's not pinned already.");
}
- mWmService.inSurfaceTransaction(() -> {
- final Task task = getBottomMostTask();
- setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ final Task task = getBottomMostTask();
+ setWindowingMode(WINDOWING_MODE_UNDEFINED);
- // Task could have been removed from the hierarchy due to windowing mode change
- // where its only child is reparented back to their original parent task.
- if (isAttached()) {
- getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
- }
+ // Task could have been removed from the hierarchy due to windowing mode change
+ // where its only child is reparented back to their original parent task.
+ if (isAttached()) {
+ getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ }
- mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
- });
+ mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
}
private int setBounds(Rect existing, Rect bounds) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index f0a66540061d..c57983c53d37 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1767,7 +1767,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
* Exposes the home task capability of the TaskDisplayArea
*/
boolean canHostHomeTask() {
- return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask;
+ return mDisplayContent.isHomeSupported() && mCanHostHomeTask;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 197edc30aa8e..8bc461f05387 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1793,7 +1793,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving, reason);
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(),
PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately, autoEnteringPip));
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index f1481760a5e7..17ab00d64924 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -394,7 +394,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
boolean taskAppearedSent = t.mTaskAppearedSent;
if (taskAppearedSent) {
if (t.getSurfaceControl() != null) {
- t.migrateToNewSurfaceControl(t.getPendingTransaction());
+ t.migrateToNewSurfaceControl(t.getSyncTransaction());
}
t.mTaskAppearedSent = false;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c0bf2ce41435..caa57bb032ca 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -173,7 +173,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
private final TransitionController mController;
private final BLASTSyncEngine mSyncEngine;
private final Token mToken;
- private IApplicationThread mRemoteAnimApp;
private @Nullable ActivityRecord mPipActivity;
@@ -1052,12 +1051,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
* @return true if we are *guaranteed* to enter-pip. This means we return false if there's
* a chance we won't thus legacy-entry (via pause+userLeaving) will return false.
*/
- private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar,
- @Nullable ActivityRecord resuming) {
+ private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar) {
if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null || !ar.isState(RESUMED)) {
return false;
}
+ final ActivityRecord resuming = getVisibleTransientLaunch(ar.getTaskDisplayArea());
if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
if (!ar.getTask().isVisibleRequested() || didCommitTransientLaunch()) {
// force enable pip-on-task-switch now that we've committed to actually launching
@@ -1196,9 +1195,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final boolean isScreenOff = ar.mDisplayContent == null
|| ar.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF;
if ((!visibleAtTransitionEnd || isScreenOff) && !ar.isVisibleRequested()) {
- final ActivityRecord resuming = getVisibleTransientLaunch(
- ar.getTaskDisplayArea());
- final boolean commitVisibility = !checkEnterPipOnFinish(ar, resuming);
+ final boolean commitVisibility = !checkEnterPipOnFinish(ar);
// Avoid commit visibility if entering pip or else we will get a sudden
// "flash" / surface going invisible for a split second.
if (commitVisibility) {
@@ -1347,6 +1344,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final DisplayContent dc =
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+ dc.getInputMonitor().updateInputWindowsLw(false /* force */);
}
if (mTransientLaunches != null) {
for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
@@ -1431,7 +1429,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (candidateActivity.getTaskDisplayArea() != taskDisplayArea) {
continue;
}
- if (!candidateActivity.isVisible()) {
+ if (!candidateActivity.isVisibleRequested()) {
continue;
}
return candidateActivity;
@@ -1487,13 +1485,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return mForcePlaying;
}
+ /** Adjusts the priority of the process which will run the transition animation. */
void setRemoteAnimationApp(IApplicationThread app) {
- mRemoteAnimApp = app;
- }
-
- /** Returns the app which will run the transition animation. */
- IApplicationThread getRemoteAnimationApp() {
- return mRemoteAnimApp;
+ final WindowProcessController wpc = mController.mAtm.getProcessController(app);
+ if (wpc != null) {
+ // This is an early prediction. If the process doesn't ack the animation in 200 ms,
+ // the priority will be restored.
+ mController.mRemotePlayer.update(wpc, true /* running */, true /* predict */);
+ }
}
void setNoAnimation(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 1f01778acd0c..a736874f178d 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1251,13 +1251,7 @@ class TransitionController {
} else if (mPlayingTransitions.isEmpty()) {
mTransitionPlayerProc.setRunningRemoteAnimation(false);
mRemotePlayer.clear();
- return;
}
- final IApplicationThread appThread = transition.getRemoteAnimationApp();
- if (appThread == null || appThread == mTransitionPlayerProc.getThread()) return;
- final WindowProcessController delegate = mAtm.getProcessController(appThread);
- if (delegate == null) return;
- mRemotePlayer.update(delegate, isPlaying, true /* predict */);
}
/** Called when a transition is aborted. This should only be called by {@link Transition} */
@@ -1483,7 +1477,7 @@ class TransitionController {
* {@link #mTransitionPlayerProc}.
*/
static class RemotePlayer {
- private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 100;
+ private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 200;
@GuardedBy("itself")
private final ArrayMap<IBinder, DelegateProcess> mDelegateProcesses = new ArrayMap<>();
private final ActivityTaskManagerService mAtm;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index e95d2651504b..fd22f15fb798 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -128,7 +128,6 @@ public class WindowAnimator {
}
ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
- mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
root.handleCompleteDeferredRemoval();
@@ -163,6 +162,7 @@ public class WindowAnimator {
dc.mLastContainsRunningSurfaceAnimator = false;
dc.enableHighFrameRate(false);
}
+ mTransaction.merge(dc.getPendingTransaction());
}
cancelAnimation();
@@ -196,8 +196,8 @@ public class WindowAnimator {
updateRunningExpensiveAnimationsLegacy();
}
- SurfaceControl.mergeToGlobalTransaction(mTransaction);
- mService.closeSurfaceTransaction("WindowAnimator");
+ mTransaction.apply();
+ mService.mWindowTracing.logState("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f6c431fe6234..e28262dfbe2f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2988,12 +2988,23 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
/** Whether we can start change transition with this window and current display status. */
boolean canStartChangeTransition() {
- return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
- && getSurfaceControl() != null && !mDisplayContent.inTransition()
- && isVisible() && isVisibleRequested() && okToAnimate()
- // Pip animation will be handled by PipTaskOrganizer.
- && !inPinnedWindowingMode() && getParent() != null
- && !getParent().inPinnedWindowingMode();
+ if (mWmService.mDisableTransitionAnimation || !okToAnimate()) return false;
+
+ // Change transition only make sense as we go from "visible" to "visible".
+ if (mDisplayContent == null || getSurfaceControl() == null
+ || !isVisible() || !isVisibleRequested()) {
+ return false;
+ }
+
+ // Make sure display isn't a part of the transition already - needed for legacy transitions.
+ if (mDisplayContent.inTransition()) return false;
+
+ if (!ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ // Screenshots are turned off when PiP is undergoing changes.
+ return !inPinnedWindowingMode() && getParent() != null
+ && !getParent().inPinnedWindowingMode();
+ }
+ return true;
}
/**
@@ -3950,7 +3961,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return true;
}
- boolean useBLASTSync() {
+ boolean syncNextBuffer() {
return mSyncState != SYNC_STATE_NONE;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 46677107c670..b3a36501b7cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -49,5 +49,8 @@ class WindowManagerFlags {
final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
+ final boolean mAllowsScreenSizeDecoupledFromStatusBarAndCutout =
+ Flags.allowsScreenSizeDecoupledFromStatusBarAndCutout();
+
/* End Available Flags */
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9f1bccb9a27a..ae171a0d8030 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -28,6 +28,7 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
@@ -45,6 +46,7 @@ import android.view.SurfaceControlViewHost;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
+import android.window.ScreenCapture;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
@@ -752,9 +754,31 @@ public abstract class WindowManagerInternal {
public abstract Context getTopFocusedDisplayUiContext();
/**
- * Checks if this display is configured and allowed to show system decorations.
+ * Sets whether the relevant display content can host the relevant home activity and wallpaper.
+ *
+ * @param displayUniqueId The unique ID of the display. Note that the display may not yet be
+ * created, but whenever it is, this property will be applied.
+ * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}.
+ * @param supported Whether home and wallpaper are supported on this display.
+ */
+ public abstract void setHomeSupportedOnDisplay(
+ @NonNull String displayUniqueId, int displayType, boolean supported);
+
+ /**
+ * Checks if this display is configured and allowed to show home activity and wallpaper.
+ *
+ * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+ * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+ */
+ public abstract boolean isHomeSupportedOnDisplay(int displayId);
+
+ /**
+ * Removes any settings relevant to the given display.
+ *
+ * <p>This may be used when a property is set for a display unique ID before the display
+ * creation but the actual display creation failed for some reason.</p>
*/
- public abstract boolean shouldShowSystemDecorOnDisplay(int displayId);
+ public abstract void clearDisplaySettings(@NonNull String displayUniqueId, int displayType);
/**
* Indicates the policy for how the display should show IME.
@@ -956,6 +980,14 @@ public abstract class WindowManagerInternal {
public abstract SurfaceControl getA11yOverlayLayer(int displayId);
/**
+ * Captures the entire display specified by the displayId using the args provided. If the args
+ * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+ */
+ public abstract void captureDisplay(int displayId,
+ @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener);
+
+ /**
* Device has a software navigation bar (separate from the status bar) on specific display.
*
* @param displayId the id of display to check if there is a software navigation bar.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ab3ddbd94224..a69a07f9aee0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -336,7 +336,6 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
-import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -661,11 +660,6 @@ public class WindowManagerService extends IWindowManager.Stub
@NonNull
final RootWindowContainer mRoot;
- // Whether the system should use BLAST for ViewRootImpl
- final boolean mUseBLAST;
- // Whether to enable BLASTSyncEngine Transaction passing.
- static final boolean USE_BLAST_SYNC = true;
-
final BLASTSyncEngine mSyncEngine;
boolean mIsPc;
@@ -1054,29 +1048,6 @@ public class WindowManagerService extends IWindowManager.Stub
SystemPerformanceHinter mSystemPerformanceHinter;
- void openSurfaceTransaction() {
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
- SurfaceControl.openTransaction();
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
- /**
- * Closes a surface transaction.
- * @param where debug string indicating where the transaction originated
- */
- void closeSurfaceTransaction(String where) {
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
- SurfaceControl.closeTransaction();
- mWindowTracing.logState(where);
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
/** Listener to notify activity manager about app transitions. */
final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
= new WindowManagerInternal.AppTransitionListener() {
@@ -1194,10 +1165,11 @@ public class WindowManagerService extends IWindowManager.Stub
.getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);
final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
.getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
- && Flags.closeToSquareConfigIncludesStatusBar();
+ && mFlags.mAllowsScreenSizeDecoupledFromStatusBarAndCutout;
if (!isScreenSizeDecoupledFromStatusBarAndCutout) {
mDecorTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars();
- mConfigTypes = WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars();
+ mConfigTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.statusBars()
+ | WindowInsets.Type.navigationBars();
} else {
mDecorTypes = WindowInsets.Type.navigationBars();
mConfigTypes = WindowInsets.Type.navigationBars();
@@ -1220,8 +1192,6 @@ public class WindowManagerService extends IWindowManager.Stub
mRoot = new RootWindowContainer(this);
final ContentResolver resolver = context.getContentResolver();
- mUseBLAST = Settings.Global.getInt(resolver,
- Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, 1) == 1;
mSyncEngine = new BLASTSyncEngine(this);
@@ -1742,13 +1712,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
// From now on, no exceptions or errors allowed!
-
- res = ADD_OKAY;
-
- if (mUseBLAST) {
- res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
- }
-
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
}
@@ -1986,7 +1949,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void removeWindow(Session session, IWindow client) {
+ void removeClientToken(Session session, IBinder client) {
synchronized (mGlobalLock) {
WindowState win = windowForClientLocked(session, client, false);
if (win != null) {
@@ -2556,7 +2519,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (outSyncIdBundle != null) {
final int maybeSyncSeqId;
- if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
+ if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
&& win.mSyncSeqId > lastSyncSeqId) {
maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
win.markRedrawForSyncReported();
@@ -5819,15 +5782,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean useBLAST() {
- return mUseBLAST;
- }
-
- public boolean useBLASTSync() {
- return USE_BLAST_SYNC;
- }
-
- @Override
public void getInitialDisplaySize(int displayId, Point size) {
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -8292,9 +8246,41 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean shouldShowSystemDecorOnDisplay(int displayId) {
+ public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType,
+ boolean supported) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mDisplayWindowSettings.setHomeSupportedOnDisplayLocked(
+ displayUniqueId, displayType, supported);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean isHomeSupportedOnDisplay(int displayId) {
synchronized (mGlobalLock) {
- return WindowManagerService.this.shouldShowSystemDecors(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that "
+ + "does not exist: %d", displayId);
+ return false;
+ }
+ return displayContent.isHomeSupported();
+ }
+ }
+
+ @Override
+ public void clearDisplaySettings(String displayUniqueId, int displayType) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
}
@@ -8425,12 +8411,13 @@ public class WindowManagerService extends IWindowManager.Stub
SurfaceControlViewHost.SurfacePackage overlay) {
if (overlay == null) {
throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId);
- } else if (overlay.getSurfaceControl() == null
- || !overlay.getSurfaceControl().isValid()) {
- throw new IllegalArgumentException(
- "Invalid overlay surfacecontrol passed in for task=" + taskId);
}
synchronized (mGlobalLock) {
+ if (overlay.getSurfaceControl() == null
+ || !overlay.getSurfaceControl().isValid()) {
+ throw new IllegalArgumentException(
+ "Invalid overlay surfacecontrol passed in for task=" + taskId);
+ }
final Task task = mRoot.getRootTask(taskId);
if (task == null) {
throw new IllegalArgumentException("no task with taskId" + taskId);
@@ -8519,6 +8506,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener) {
+ WindowManagerService.this.captureDisplay(displayId, captureArgs, listener);
+ }
+
+ @Override
public boolean hasNavigationBar(int displayId) {
return WindowManagerService.this.hasNavigationBar(displayId);
}
@@ -8575,39 +8568,6 @@ public class WindowManagerService extends IWindowManager.Stub
mAppFreezeListeners.remove(listener);
}
- /**
- * WARNING: This interrupts surface updates, be careful! Don't
- * execute within the transaction for longer than you would
- * execute on an animation thread.
- * WARNING: This method contains locks known to the State of California
- * to cause Deadlocks and other conditions.
- *
- * Begins a surface transaction with which the AM can batch operations.
- * All Surface updates performed by the WindowManager following this
- * will not appear on screen until after the call to
- * closeSurfaceTransaction.
- *
- * ActivityManager can use this to ensure multiple 'commands' will all
- * be reflected in a single frame. For example when reparenting a window
- * which was previously hidden due to it's parent properties, we may
- * need to ensure it is hidden in the same frame that the properties
- * from the new parent are inherited, otherwise it could be revealed
- * mistakenly.
- *
- * TODO(b/36393204): We can investigate totally replacing #deferSurfaceLayout
- * with something like this but it seems that some existing cases of
- * deferSurfaceLayout may be a little too broad, in particular the total
- * enclosure of startActivityUnchecked which could run for quite some time.
- */
- void inSurfaceTransaction(Runnable exec) {
- SurfaceControl.openTransaction();
- try {
- exec.run();
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
/** Called to inform window manager if non-Vr UI shoul be disabled or not. */
public void disableNonVrUi(boolean disable) {
synchronized (mGlobalLock) {
@@ -8922,7 +8882,7 @@ public class WindowManagerService extends IWindowManager.Stub
* views.
*/
void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
- SurfaceControl surface, IWindow window, IBinder hostInputToken,
+ SurfaceControl surface, IBinder clientToken, IBinder hostInputToken,
int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken,
IBinder inputTransferToken, String inputHandleName, InputChannel outInputChannel) {
final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type);
@@ -8931,7 +8891,7 @@ public class WindowManagerService extends IWindowManager.Stub
Objects.requireNonNull(outInputChannel);
synchronized (mGlobalLock) {
EmbeddedWindowController.EmbeddedWindow win =
- new EmbeddedWindowController.EmbeddedWindow(session, this, window,
+ new EmbeddedWindowController.EmbeddedWindow(session, this, clientToken,
mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
sanitizedType, displayId, inputTransferToken, inputHandleName,
(flags & FLAG_NOT_FOCUSABLE) == 0);
@@ -8943,7 +8903,7 @@ public class WindowManagerService extends IWindowManager.Stub
updateInputChannel(outInputChannel.getToken(), callingUid, callingPid, displayId, surface,
name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType,
- null /* region */, window);
+ null /* region */, clientToken);
}
boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
@@ -9018,10 +8978,10 @@ public class WindowManagerService extends IWindowManager.Stub
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,
- int privateFlags, int inputFeatures, int type, Region region, IWindow window) {
+ int privateFlags, int inputFeatures, int type, Region region, IBinder clientToken) {
final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
- h.setWindowToken(window);
+ h.setWindowToken(clientToken);
h.name = name;
flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 89d47bcf41d5..208df6c768bf 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -63,6 +63,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -938,7 +939,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
final Task task = wc.asTask();
- task.remove(true, "Applying remove task Hierarchy Op");
+
+ if (task.isLeafTask()) {
+ mService.mTaskSupervisor
+ .removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task"
+ + "-through-hierarchyOp");
+ } else {
+ mService.mTaskSupervisor.removeRootTask(task);
+ }
break;
}
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index a74a707d5ef9..558bf9d6310a 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1666,7 +1666,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private void scheduleClientTransactionItem(@NonNull IApplicationThread thread,
@NonNull ClientTransactionItem transactionItem) {
try {
- mAtm.getLifecycleManager().scheduleTransaction(thread, transactionItem);
+ mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem="
+ transactionItem + " owner=" + mOwner, e);
@@ -1864,6 +1864,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
+ public boolean isShowingUiWhileDozing() {
+ return this == mAtm.mVisibleDozeUiProcess;
+ }
+
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean isPreviousProcess() {
return this == mAtm.mPreviousProcess;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4e17011c7141..3e43908994ad 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,7 +29,6 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.SurfaceControl.Transaction;
-import static android.view.SurfaceControl.getGlobalTransaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -1693,8 +1692,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public IWindow getIWindow() {
- return mClient;
+ public IBinder getWindowToken() {
+ return mClient.asBinder();
}
@Override
@@ -2794,7 +2793,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
}
if (!isVisibleByPolicy()) {
- mWinAnimator.hide(getGlobalTransaction(), "checkPolicyVisibilityChange");
+ mWinAnimator.hide(getPendingTransaction(), "checkPolicyVisibilityChange");
if (isFocused()) {
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
"setAnimationLocked: setting mFocusMayChange true");
@@ -3368,7 +3367,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mAnimatingExit = false;
ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
- if (useBLASTSync()) {
+ if (syncNextBuffer()) {
immediatelyNotifyBlastSync();
}
}
@@ -5799,7 +5798,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Consume the transaction because the sync group will merge it.
postDrawTransaction = null;
}
- } else if (useBLASTSync()) {
+ } else if (syncNextBuffer()) {
// Sync that is not using BLAST
layoutNeeded = onSyncFinishedDrawing();
}
@@ -5855,7 +5854,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// drawing for being visible, then no need to request redraw.
return false;
}
- return useBLASTSync();
+ return syncNextBuffer();
}
int getSyncMethod() {
@@ -5880,11 +5879,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* it's next draw in to a transaction). If we have pending draw handlers, we are
* looking for the client to sync.
*
- * See {@link WindowState#mPendingDrawHandlers}
+ * See {@link WindowState#mDrawHandlers}
*/
@Override
- boolean useBLASTSync() {
- return super.useBLASTSync() || (mDrawHandlers.size() != 0);
+ boolean syncNextBuffer() {
+ return super.syncNextBuffer() || (mDrawHandlers.size() != 0);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 209d93421fc1..d348491b3d2a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -20,7 +20,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.SurfaceControl.METADATA_OWNER_PID;
import static android.view.SurfaceControl.METADATA_OWNER_UID;
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
-import static android.view.SurfaceControl.getGlobalTransaction;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
@@ -37,7 +36,6 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.WindowContentFrameStats;
-import android.view.WindowManager;
import com.android.internal.protolog.common.ProtoLog;
@@ -73,7 +71,7 @@ class WindowSurfaceController {
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
- final SurfaceControl.Builder b = win.makeSurface()
+ mSurfaceControl = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
.setFormat(format)
@@ -81,16 +79,8 @@ class WindowSurfaceController {
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
- .setCallsite("WindowSurfaceController");
-
- final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
-
- if (useBLAST) {
- b.setBLASTLayer();
- }
-
- mSurfaceControl = b.build();
+ .setCallsite("WindowSurfaceController")
+ .setBLASTLayer().build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -157,14 +147,9 @@ class WindowSurfaceController {
if (mSurfaceControl == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setOpaque(mSurfaceControl, isOpaque);
- } finally {
- mService.closeSurfaceTransaction("setOpaqueLocked");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
- }
+
+ mAnimator.mWin.getPendingTransaction().setOpaque(mSurfaceControl, isOpaque);
+ mService.scheduleAnimationLocked();
}
void setSecure(boolean isSecure) {
@@ -174,18 +159,15 @@ class WindowSurfaceController {
return;
}
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setSecure(mSurfaceControl, isSecure);
- final DisplayContent dc = mAnimator.mWin.mDisplayContent;
- if (dc != null) {
- dc.refreshImeSecureFlag(getGlobalTransaction());
- }
- } finally {
- mService.closeSurfaceTransaction("setSecure");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
+ final SurfaceControl.Transaction t = mAnimator.mWin.getPendingTransaction();
+ t.setSecure(mSurfaceControl, isSecure);
+
+ final DisplayContent dc = mAnimator.mWin.mDisplayContent;
+ if (dc != null) {
+ dc.refreshImeSecureFlag(t);
}
+ mService.scheduleAnimationLocked();
}
void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a7d77304d046..f1cddc643422 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1293,6 +1293,11 @@ void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled)
}
void NativeInputManager::setShowTouches(bool enabled) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setShowTouchesEnabled(enabled);
+ return;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -1744,6 +1749,11 @@ FloatPoint NativeInputManager::getMouseCursorPosition() {
}
void NativeInputManager::setStylusPointerIconEnabled(bool enabled) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setStylusPointerIconEnabled(enabled);
+ return;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -2547,11 +2557,10 @@ static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj,
static void nativeSetKeyRepeatConfiguration(JNIEnv* env, jobject nativeImplObj, jint timeoutMs,
jint delayMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(static_cast<nsecs_t>(
- timeoutMs) *
- 1000000,
- static_cast<nsecs_t>(delayMs) *
- 1000000);
+ im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(std::chrono::milliseconds(
+ timeoutMs),
+ std::chrono::milliseconds(
+ delayMs));
}
static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, jint version,
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 80427b346f1a..505421e81d3d 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -494,12 +494,21 @@ JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aid
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
const std::shared_ptr<TvInputCallbackWrapper>& in_callback) {
if (mIsHidl) {
- in_callback->aidlTvInputCallback = nullptr;
- return hidlSetCallback(in_callback == nullptr ? nullptr : in_callback->hidlTvInputCallback);
+ if (in_callback == nullptr) {
+ return hidlSetCallback(nullptr);
+ }
+ else {
+ in_callback->aidlTvInputCallback = nullptr;
+ return hidlSetCallback(in_callback->hidlTvInputCallback);
+ }
} else {
- in_callback->hidlTvInputCallback = nullptr;
- return mAidlTvInput->setCallback(in_callback == nullptr ? nullptr
- : in_callback->aidlTvInputCallback);
+ if (in_callback == nullptr) {
+ return mAidlTvInput->setCallback(nullptr);
+ }
+ else {
+ in_callback->hidlTvInputCallback = nullptr;
+ return mAidlTvInput->setCallback(in_callback->aidlTvInputCallback);
+ }
}
}
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 215934f4dc09..cca4261795ab 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -455,6 +455,20 @@
<xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
+ <!-- list of supported modes when sensor is ON. Each point corresponds to one mode.
+ Mode format is : first = refreshRate, second = vsyncRate. E.g. :
+ <supportedModes>
+ <point>
+ <first>60</first> // refreshRate
+ <second>60</second> //vsyncRate
+ </point>
+ ....
+ </supportedModes>
+ -->
+ <xs:element type="nonNegativeFloatToFloatMap" name="supportedModes" minOccurs="0">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index f7e004375071..f767291b4953 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -349,9 +349,11 @@ package com.android.server.display.config {
ctor public SensorDetails();
method @Nullable public final String getName();
method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate();
+ method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getSupportedModes();
method @Nullable public final String getType();
method public final void setName(@Nullable String);
method public final void setRefreshRate(@Nullable com.android.server.display.config.RefreshRateRange);
+ method public final void setSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap);
method public final void setType(@Nullable String);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34d67551d49f..9b62a2c41655 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9667,6 +9667,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ @Override
+ public ComponentName getDeviceOwnerComponentOnUser(int userId) {
+ if (!mHasFeature) {
+ return null;
+ }
+ if (mInjector.userHandleGetCallingUserId() != userId) {
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ }
+ synchronized (getLockObject()) {
+ // There is only ever one device owner on a device so if the passed userId is the same
+ // as the device owner userId we know that the componentName returned by
+ // getDeviceOwnerComponent will be the correct one.
+ if (mOwners.getDeviceOwnerUserId() == userId || userId == UserHandle.USER_ALL) {
+ return mOwners.getDeviceOwnerComponent();
+ }
+ }
+ return null;
+ }
+
private int getDeviceOwnerUserIdUncheckedLocked() {
return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index 26ea9d24f918..f94a0d664a69 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -145,16 +145,6 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
opSparseArray
}
- override fun areUidModesDefault(uid: Int): Boolean {
- val modes = getUidModes(uid)
- return modes == null || modes.isEmpty()
- }
-
- override fun arePackageModesDefault(packageName: String, userId: Int): Boolean {
- val modes = service.getState { getPackageModes(packageName, userId) }
- return modes == null || modes.isEmpty()
- }
-
override fun clearAllModes() {
// We don't need to implement this because it's only called in AppOpsService#readState
// and we have our own persistence.
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 010604f9aaaa..02032c786563 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
@@ -1706,7 +1706,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
/** Listener for permission flags changes. */
- abstract class OnPermissionFlagsChangedListener {
+ interface OnPermissionFlagsChangedListener {
/**
* Called when a permission flags change has been made to the upcoming new state.
*
@@ -1714,7 +1714,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
* and only call external code after [onStateMutated] when the new state has actually become
* the current state visible to external code.
*/
- abstract fun onPermissionFlagsChanged(
+ fun onPermissionFlagsChanged(
appId: Int,
userId: Int,
permissionName: String,
@@ -1727,6 +1727,6 @@ class AppIdPermissionPolicy : SchemePolicy() {
*
* Implementations should keep this method fast to avoid stalling the locked state mutation.
*/
- abstract fun onStateMutated()
+ fun onStateMutated()
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 15a58593432e..bb68bc5c791d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,7 +16,9 @@
package com.android.server.permission.access.permission
+import android.Manifest
import android.permission.PermissionManager
+import android.permission.flags.Flags
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -261,10 +263,6 @@ class DevicePermissionPolicy : SchemePolicy() {
synchronized(listenersLock) { listeners = listeners + listener }
}
- fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) { listeners = listeners - listener }
- }
-
private fun isDeviceAwarePermission(permissionName: String): Boolean =
DEVICE_AWARE_PERMISSIONS.contains(permissionName)
@@ -273,14 +271,16 @@ class DevicePermissionPolicy : SchemePolicy() {
/** These permissions are supported for virtual devices. */
// TODO: b/298661870 - Use new API to get the list of device aware permissions.
- val DEVICE_AWARE_PERMISSIONS = emptySet<String>()
+ val DEVICE_AWARE_PERMISSIONS =
+ if (Flags.deviceAwarePermissionApis()) {
+ setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
+ } else {
+ emptySet<String>()
+ }
}
- /**
- * TODO: b/289355341 - implement listener for permission changes Listener for permission flags
- * changes.
- */
- abstract class OnDevicePermissionFlagsChangedListener {
+ /** Listener for permission flags changes. */
+ interface OnDevicePermissionFlagsChangedListener {
/**
* Called when a permission flags change has been made to the upcoming new state.
*
@@ -288,7 +288,7 @@ class DevicePermissionPolicy : SchemePolicy() {
* and only call external code after [onStateMutated] when the new state has actually become
* the current state visible to external code.
*/
- abstract fun onDevicePermissionFlagsChanged(
+ fun onDevicePermissionFlagsChanged(
appId: Int,
userId: Int,
deviceId: String,
@@ -302,6 +302,6 @@ class DevicePermissionPolicy : SchemePolicy() {
*
* Implementations should keep this method fast to avoid stalling the locked state mutation.
*/
- abstract fun onStateMutated()
+ fun onStateMutated()
}
}
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 ab3d78c9958c..0d196b48b3f2 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
@@ -19,6 +19,7 @@ package com.android.server.permission.access.permission
import android.Manifest
import android.app.ActivityManager
import android.app.AppOpsManager
+import android.companion.virtual.VirtualDeviceManager
import android.compat.annotation.ChangeId
import android.compat.annotation.EnabledAfter
import android.content.Context
@@ -169,6 +170,7 @@ class PermissionService(private val service: AccessCheckingService) :
onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
onPermissionFlagsChangedListener = OnPermissionFlagsChangedListener()
policy.addOnPermissionFlagsChangedListener(onPermissionFlagsChangedListener)
+ devicePolicy.addOnPermissionFlagsChangedListener(onPermissionFlagsChangedListener)
}
override fun getAllPermissionGroups(flags: Int): List<PermissionGroupInfo> {
@@ -2616,10 +2618,11 @@ class PermissionService(private val service: AccessCheckingService) :
/** Callback invoked when interesting actions have been taken on a permission. */
private inner class OnPermissionFlagsChangedListener :
- AppIdPermissionPolicy.OnPermissionFlagsChangedListener() {
+ AppIdPermissionPolicy.OnPermissionFlagsChangedListener,
+ DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener {
private var isPermissionFlagsChanged = false
- private val runtimePermissionChangedUids = MutableIntSet()
+ private val runtimePermissionChangedUidDevices = MutableIntMap<MutableSet<String>>()
// Mapping from UID to whether only notifications permissions are revoked.
private val runtimePermissionRevokedUids = SparseBooleanArray()
private val gidsChangedUids = MutableIntSet()
@@ -2642,6 +2645,24 @@ class PermissionService(private val service: AccessCheckingService) :
oldFlags: Int,
newFlags: Int
) {
+ onDevicePermissionFlagsChanged(
+ appId,
+ userId,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,
+ permissionName,
+ oldFlags,
+ newFlags
+ )
+ }
+
+ override fun onDevicePermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ deviceId: String,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ ) {
isPermissionFlagsChanged = true
val uid = UserHandle.getUid(userId, appId)
@@ -2655,12 +2676,12 @@ class PermissionService(private val service: AccessCheckingService) :
// permission flags have changed for a non-runtime permission, now we no longer do
// that because permission flags are only for runtime permissions and the listeners
// aren't being notified of non-runtime permission grant state changes anyway.
- runtimePermissionChangedUids += uid
if (wasPermissionGranted && !isPermissionGranted) {
runtimePermissionRevokedUids[uid] =
permissionName in NOTIFICATIONS_PERMISSIONS &&
runtimePermissionRevokedUids.get(uid, true)
}
+ runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId
}
if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) {
@@ -2674,10 +2695,12 @@ class PermissionService(private val service: AccessCheckingService) :
isPermissionFlagsChanged = false
}
- runtimePermissionChangedUids.forEachIndexed { _, uid ->
- onPermissionsChangeListeners.onPermissionsChanged(uid)
+ runtimePermissionChangedUidDevices.forEachIndexed { _, uid, deviceIds ->
+ deviceIds.forEach { deviceId ->
+ onPermissionsChangeListeners.onPermissionsChanged(uid, deviceId)
+ }
}
- runtimePermissionChangedUids.clear()
+ runtimePermissionChangedUidDevices.clear()
if (!isKillRuntimePermissionRevokedUidsSkipped) {
val reason =
@@ -2749,15 +2772,16 @@ class PermissionService(private val service: AccessCheckingService) :
when (msg.what) {
MSG_ON_PERMISSIONS_CHANGED -> {
val uid = msg.arg1
- handleOnPermissionsChanged(uid)
+ val deviceId = msg.obj as String
+ handleOnPermissionsChanged(uid, deviceId)
}
}
}
- private fun handleOnPermissionsChanged(uid: Int) {
+ private fun handleOnPermissionsChanged(uid: Int, deviceId: String) {
listeners.broadcast { listener ->
try {
- listener.onPermissionsChanged(uid)
+ listener.onPermissionsChanged(uid, deviceId)
} catch (e: RemoteException) {
Slog.e(LOG_TAG, "Error when calling OnPermissionsChangeListener", e)
}
@@ -2772,9 +2796,9 @@ class PermissionService(private val service: AccessCheckingService) :
listeners.unregister(listener)
}
- fun onPermissionsChanged(uid: Int) {
+ fun onPermissionsChanged(uid: Int, deviceId: String) {
if (listeners.registeredCallbackCount > 0) {
- obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget()
+ obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget()
}
}
diff --git a/services/proguard.flags b/services/proguard.flags
index 261bb7cacdc4..88561b460b05 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -14,13 +14,20 @@
}
# APIs referenced by dependent JAR files and modules
--keep @interface android.annotation.SystemApi
+# TODO(b/300514883): Pull @SystemApi keep rules from system-api.pro.
+-keep interface android.annotation.SystemApi
-keep @android.annotation.SystemApi class * {
public protected *;
}
-keepclasseswithmembers class * {
@android.annotation.SystemApi *;
}
+# Also ensure nested classes are kept. This is overly conservative, but handles
+# cases where such classes aren't explicitly marked @SystemApi.
+-if @android.annotation.SystemApi class *
+-keep public class <1>$** {
+ public protected *;
+}
# Derivatives of SystemService and other services created via reflection
-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
@@ -38,10 +45,6 @@
public static void write(...);
}
-# Binder interfaces
--keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
--keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
-
# Various classes subclassed in or referenced via JNI in ethernet-service
-keep public class android.net.** { *; }
-keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 6c24d6d6e5a6..820628c98dee 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -21,8 +21,8 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
- <option name="test-file-name" value="FrameworksImeTests.apk" />
<option name="test-file-name" value="SimpleTestIme.apk" />
+ <option name="test-file-name" value="FrameworksImeTests.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksImeTests" />
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index b63a58a96b8c..21342783b79c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
import android.content.Context;
@@ -48,6 +49,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper;
import com.android.apps.inputmethod.simpleime.testing.TestActivity;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import org.junit.After;
import org.junit.Before;
@@ -635,6 +637,82 @@ public class InputMethodServiceTest {
.getRootWindowInsets().getInsetsIgnoringVisibility(captionBar()));
}
+ /**
+ * This checks that trying to show and hide the navigation bar takes effect
+ * when the IME does draw the IME navigation bar.
+ */
+ @Test
+ public void testShowHideImeNavigationBar_doesDrawImeNavBar() throws Exception {
+ boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService()
+ .hasNavigationBar(mInputMethodService.getDisplayId());
+ assumeTrue("Must have a navigation bar", hasNavigationBar);
+
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Show IME
+ verifyInputViewStatusOnMainSync(
+ () -> {
+ mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged(
+ InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR
+ | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN
+ );
+ assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue();
+ },
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue();
+
+ // Try to hide IME nav bar
+ mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow()
+ .getInsetsController().hide(captionBar()));
+ mInstrumentation.waitForIdleSync();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
+
+ // Try to show IME nav bar
+ mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow()
+ .getInsetsController().show(captionBar()));
+ mInstrumentation.waitForIdleSync();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue();
+ }
+ /**
+ * This checks that trying to show and hide the navigation bar has no effect
+ * when the IME does not draw the IME navigation bar.
+ *
+ * Note: The IME navigation bar is *never* visible in 3 button navigation mode.
+ */
+ @Test
+ public void testShowHideImeNavigationBar_doesNotDrawImeNavBar() throws Exception {
+ boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService()
+ .hasNavigationBar(mInputMethodService.getDisplayId());
+ assumeTrue("Must have a navigation bar", hasNavigationBar);
+
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Show IME
+ verifyInputViewStatusOnMainSync(() -> {
+ mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged(
+ 0 /* navButtonFlags */);
+ assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue();
+ },
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
+
+ // Try to hide IME nav bar
+ mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow()
+ .getInsetsController().hide(captionBar()));
+ mInstrumentation.waitForIdleSync();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
+
+ // Try to show IME nav bar
+ mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow()
+ .getInsetsController().show(captionBar()));
+ mInstrumentation.waitForIdleSync();
+ assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
+ }
+
private void verifyInputViewStatus(
Runnable runnable, boolean expected, boolean inputViewStarted)
throws InterruptedException {
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 12cd0f6e1a7c..8d76fddcc793 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager
import android.os.Binder
import android.os.UserHandle
import android.util.ArrayMap
+import com.android.internal.pm.pkg.component.ParsedActivity
import com.android.server.pm.AppsFilterImpl
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageManagerServiceInjector
@@ -39,7 +40,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.component.ParsedActivity
import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.snapshot.PackageDataSnapshot
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index d5cd6ef9eb69..25146a87970f 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -49,16 +49,16 @@ import android.util.SparseArray;
import androidx.annotation.NonNull;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
import com.android.server.pm.pkg.component.ParsedComponentImpl;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 0eac4e6a25ac..7c28e13f0eee 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,16 @@ import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -69,24 +79,14 @@ import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 9e371649214c..7123c2076640 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,16 +38,16 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.pm.test.service.server.R;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+import com.android.server.pm.test.service.server.R;
import com.google.common.truth.Expect;
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 edab1d649ac6..170faf61858b 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
@@ -270,7 +270,8 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::getMinAspectRatio,
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
- AndroidPackage::hasRequestRawExternalStorageAccess
+ AndroidPackage::hasRequestRawExternalStorageAccess,
+ AndroidPackage::isUpdatableSystem
)
override fun extraParams() = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 0e2e35f25b15..26468544e8d6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -17,7 +17,7 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.ActivityInfo
-import com.android.server.pm.pkg.component.ParsedActivity
+import com.android.internal.pm.pkg.component.ParsedActivity
import com.android.server.pm.pkg.component.ParsedActivityImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
index 4e44e96aa710..52d5b3bccb72 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedAttribution
+import com.android.internal.pm.pkg.component.ParsedAttribution
import com.android.server.pm.pkg.component.ParsedAttributionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
index 058f6d69f3e7..af0c0de2db15 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -17,7 +17,7 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.PackageManager
-import com.android.server.pm.pkg.component.ParsedComponent
+import com.android.internal.pm.pkg.component.ParsedComponent
import com.android.server.pm.pkg.component.ParsedComponentImpl
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
import android.os.Bundle
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
index eeb30b70c143..dc0f194f10cc 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedInstrumentation
+import com.android.internal.pm.pkg.component.ParsedInstrumentation
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
index f27a51f63049..5224f23d38d1 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedIntentInfo
+import com.android.internal.pm.pkg.component.ParsedIntentInfo
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
import android.os.Parcelable
import android.os.PatternMatcher
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
index a0d8c44899d8..dfff6025e2eb 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedMainComponent
+import com.android.internal.pm.pkg.component.ParsedMainComponent
import com.android.server.pm.pkg.component.ParsedMainComponentImpl
import android.os.Parcelable
import java.util.Arrays
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index f266e7616ff3..ccbf558734d3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
index c72a44e4c4e0..2814783b6849 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -16,8 +16,8 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
import com.android.server.pm.pkg.component.ParsedPermissionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 8b9361a31d0a..2e9604696acb 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProcess
import com.android.server.pm.pkg.component.ParsedProcessImpl
import android.util.ArrayMap
import kotlin.contracts.ExperimentalContracts
@@ -45,7 +45,7 @@ class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedPr
override fun extraParams() = listOf(
getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")),
getter(ParsedProcess::getAppClassNamesByPackage, ArrayMap<String, String>().apply {
- put("package1", "classname1");
+ put("package1", "classname1")
}),
)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 0302d5785d8f..290dbd6277b6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -17,14 +17,14 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.PathPermission
-import com.android.server.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedProvider
import com.android.server.pm.pkg.component.ParsedProviderImpl
import android.os.PatternMatcher
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
-class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class) {
-
+class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class)
+{
override val defaultImpl =
ParsedProviderImpl()
override val creator = ParsedProviderImpl.CREATOR
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index e2c9439df9cf..3ae7e9220cc6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedService
+import com.android.internal.pm.pkg.component.ParsedService
import com.android.server.pm.pkg.component.ParsedServiceImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
index ad607366967f..67dfc6d4f7ef 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedUsesPermission
+import com.android.internal.pm.pkg.component.ParsedUsesPermission
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
index d217d63c5b44..1da3a2234ffc 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
@@ -24,26 +24,25 @@ import android.content.pm.SharedLibraryInfo
import android.content.pm.VersionedPackage
import android.os.PatternMatcher
import android.util.ArraySet
+import com.android.internal.pm.pkg.component.ParsedActivity
+import com.android.internal.pm.pkg.component.ParsedInstrumentation
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedService
import com.android.server.pm.PackageSetting
import com.android.server.pm.PackageSettingBuilder
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.PackageUserStateImpl
-import com.android.server.pm.pkg.component.ParsedActivity
import com.android.server.pm.pkg.component.ParsedActivityImpl
import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedInstrumentation
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionImpl
-import com.android.server.pm.pkg.component.ParsedProcess
import com.android.server.pm.pkg.component.ParsedProcessImpl
-import com.android.server.pm.pkg.component.ParsedProvider
import com.android.server.pm.pkg.component.ParsedProviderImpl
-import com.android.server.pm.pkg.component.ParsedService
import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest
import com.google.common.truth.Expect
import org.junit.Rule
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
index ec84bc329674..316f3387c539 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
@@ -26,6 +26,8 @@ import android.os.Bundle
import android.util.ArrayMap
import android.util.SparseArray
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.permission.access.MutableAccessState
@@ -39,8 +41,6 @@ import com.android.server.pm.parsing.PackageInfoUtils
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 6e4069fbe4bd..3bafe7296fff 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -7,19 +7,12 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-// Include all test java files.
-filegroup {
- name: "displayservicetests-sources",
- srcs: [
- "src/**/*.java",
- ],
-}
-
android_test {
name: "DisplayServiceTests",
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
],
libs: [
@@ -33,6 +26,8 @@ android_test {
"frameworks-base-testutils",
"junit",
"junit-params",
+ "kotlin-test",
+ "mockito-kotlin2",
"mockingservicestests-utils-mockito",
"platform-compat-test-rules",
"platform-test-annotations",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index a400f1243afb..eb6e8b4469f0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -44,6 +44,7 @@ public class DisplayBrightnessStateTest {
float brightness = 0.3f;
float sdrBrightness = 0.2f;
boolean shouldUseAutoBrightness = true;
+ boolean shouldUpdateScreenBrightnessSetting = true;
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
brightnessReason.setModifier(BrightnessReason.MODIFIER_DIMMED);
@@ -52,12 +53,15 @@ public class DisplayBrightnessStateTest {
.setSdrBrightness(sdrBrightness)
.setBrightnessReason(brightnessReason)
.setShouldUseAutoBrightness(shouldUseAutoBrightness)
+ .setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting)
.build();
assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
assertEquals(displayBrightnessState.getSdrBrightness(), sdrBrightness, FLOAT_DELTA);
assertEquals(displayBrightnessState.getBrightnessReason(), brightnessReason);
assertEquals(displayBrightnessState.getShouldUseAutoBrightness(), shouldUseAutoBrightness);
+ assertEquals(shouldUpdateScreenBrightnessSetting,
+ displayBrightnessState.shouldUpdateScreenBrightnessSetting());
assertEquals(displayBrightnessState.toString(), getString(displayBrightnessState));
}
@@ -71,6 +75,7 @@ public class DisplayBrightnessStateTest {
.setBrightness(0.26f)
.setSdrBrightness(0.23f)
.setShouldUseAutoBrightness(false)
+ .setShouldUpdateScreenBrightnessSetting(true)
.build();
DisplayBrightnessState state2 = DisplayBrightnessState.Builder.from(state1).build();
assertEquals(state1, state2);
@@ -92,7 +97,9 @@ public class DisplayBrightnessStateTest {
.append("\n maxBrightness:")
.append(displayBrightnessState.getMaxBrightness())
.append("\n customAnimationRate:")
- .append(displayBrightnessState.getCustomAnimationRate());
+ .append(displayBrightnessState.getCustomAnimationRate())
+ .append("\n shouldUpdateScreenBrightnessSetting:")
+ .append(displayBrightnessState.shouldUpdateScreenBrightnessSetting());
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 179a9d5f748e..0bcbeb9b8a85 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -17,9 +17,12 @@
package com.android.server.display;
+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;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -526,6 +529,26 @@ public final class DisplayDeviceConfigTest {
}
@Test
+ public void testProximitySensorWithRefreshRatesFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(
+ getContent(getValidLuxThrottling(), getValidProxSensorWithRefreshRateAndVsyncRate(),
+ /* includeIdleMode= */ true));
+ assertEquals("test_proximity_sensor",
+ mDisplayDeviceConfig.getProximitySensor().type);
+ assertEquals("Test Proximity Sensor",
+ mDisplayDeviceConfig.getProximitySensor().name);
+ assertEquals(mDisplayDeviceConfig.getProximitySensor().minRefreshRate, 60, SMALL_DELTA);
+ assertEquals(mDisplayDeviceConfig.getProximitySensor().maxRefreshRate, 90, SMALL_DELTA);
+ assertThat(mDisplayDeviceConfig.getProximitySensor().supportedModes).hasSize(2);
+ SupportedMode mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
+ assertEquals(mode.refreshRate, 60, SMALL_DELTA);
+ assertEquals(mode.vsyncRate, 65, SMALL_DELTA);
+ mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(1);
+ assertEquals(mode.refreshRate, 120, SMALL_DELTA);
+ assertEquals(mode.vsyncRate, 125, SMALL_DELTA);
+ }
+
+ @Test
public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile();
@@ -821,6 +844,27 @@ public final class DisplayDeviceConfigTest {
+ "</proxSensor>\n";
}
+ private String getValidProxSensorWithRefreshRateAndVsyncRate() {
+ return "<proxSensor>\n"
+ + "<type>test_proximity_sensor</type>\n"
+ + "<name>Test Proximity Sensor</name>\n"
+ + "<refreshRate>\n"
+ + "<minimum>60</minimum>\n"
+ + "<maximum>90</maximum>\n"
+ + "</refreshRate>\n"
+ + "<supportedModes>\n"
+ + "<point>\n"
+ + "<first>60</first>\n" // refreshRate
+ + "<second>65</second>\n" //vsyncRate
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>120</first>\n" // refreshRate
+ + "<second>125</second>\n" //vsyncRate
+ + "</point>\n"
+ + "</supportedModes>"
+ + "</proxSensor>\n";
+ }
+
private String getProxSensorWithEmptyValues() {
return "<proxSensor>\n"
+ "<type></type>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 303525814435..353a7bb580ec 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -29,7 +29,7 @@ import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.server.display.DisplayManagerService.ENABLE_ON_CONNECT;
+import static com.android.server.display.ExternalDisplayPolicy.ENABLE_ON_CONNECT;
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
import static com.google.common.truth.Truth.assertThat;
@@ -123,6 +123,7 @@ import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.DeviceStateListener;
import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.input.InputManagerInternal;
@@ -195,7 +196,8 @@ public class DisplayManagerServiceTest {
@Rule(order = 1)
public Expect expect = Expect.create();
@Rule
- public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public SetFlagsRule mSetFlagsRule =
+ new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
private Context mContext;
@@ -2317,11 +2319,8 @@ public class DisplayManagerServiceTest {
String testSensorType = "testType";
Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName);
- DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData();
- sensorData.type = testSensorType;
- sensorData.name = testSensorName;
- sensorData.minRefreshRate = 10f;
- sensorData.maxRefreshRate = 100f;
+ SensorData sensorData = new SensorData(testSensorType, testSensorName,
+ /* minRefreshRate= */ 10f, /* maxRefreshRate= */ 100f);
when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(sensorData);
when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList(
@@ -2352,12 +2351,6 @@ public class DisplayManagerServiceTest {
String testSensorType = "testType";
Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName);
- DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData();
- sensorData.type = testSensorType;
- sensorData.name = testSensorName;
- sensorData.minRefreshRate = 10f;
- sensorData.maxRefreshRate = 100f;
-
when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(null);
when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList(
testSensor));
@@ -2761,8 +2754,7 @@ public class DisplayManagerServiceTest {
DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
- assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo(
- mockDisplayOffloader);
+ assertThat(display.getDisplayOffloadSessionLocked()).isNotNull();
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
new file mode 100644
index 000000000000..dea838d3763d
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+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.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class DisplayOffloadSessionImplTest {
+
+ @Mock
+ private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+
+ @Mock
+ private DisplayPowerControllerInterface mDisplayPowerController;
+
+ private DisplayOffloadSessionImpl mSession;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mDisplayOffloader.startOffload()).thenReturn(true);
+ mSession = new DisplayOffloadSessionImpl(mDisplayOffloader, mDisplayPowerController);
+ }
+
+ @Test
+ public void testStartOffload() {
+ mSession.startOffload();
+ assertTrue(mSession.isActive());
+
+ // An active session shouldn't be started again
+ mSession.startOffload();
+ verify(mDisplayOffloader, times(1)).startOffload();
+ }
+
+ @Test
+ public void testStopOffload() {
+ mSession.startOffload();
+ mSession.stopOffload();
+
+ assertFalse(mSession.isActive());
+ verify(mDisplayPowerController).setBrightnessFromOffload(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+ // An inactive session shouldn't be stopped again
+ mSession.stopOffload();
+ verify(mDisplayOffloader, times(1)).stopOffload();
+ }
+
+ @Test
+ public void testUpdateBrightness_sessionInactive() {
+ mSession.updateBrightness(0.3f);
+ verify(mDisplayPowerController, never()).setBrightnessFromOffload(anyFloat());
+ }
+
+ @Test
+ public void testUpdateBrightness_sessionActive() {
+ float brightness = 0.3f;
+
+ mSession.startOffload();
+ mSession.updateBrightness(brightness);
+
+ verify(mDisplayPowerController).setBrightnessFromOffload(brightness);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 47521d13e49c..693cafefa2c0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -77,6 +77,7 @@ import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.feature.flags.Flags;
import com.android.server.display.layout.Layout;
@@ -126,8 +127,6 @@ public final class DisplayPowerController2Test {
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
- private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
- private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -147,6 +146,8 @@ public final class DisplayPowerController2Test {
private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
@Mock
private DisplayManagerFlags mDisplayManagerFlagsMock;
+ @Mock
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
@@ -1159,19 +1160,19 @@ public final class DisplayPowerController2Test {
any(AutomaticBrightnessController.Callbacks.class),
any(Looper.class),
eq(mSensorManagerMock),
- any(),
+ /* lightSensor= */ any(),
eq(mHolder.brightnessMappingStrategy),
- anyInt(),
- anyFloat(),
- anyFloat(),
- anyFloat(),
- anyInt(),
- anyInt(),
- anyLong(),
- anyLong(),
- anyLong(),
- anyLong(),
- anyBoolean(),
+ /* lightSensorWarmUpTime= */ anyInt(),
+ /* brightnessMin= */ anyFloat(),
+ /* brightnessMax= */ anyFloat(),
+ /* dozeScaleFactor */ anyFloat(),
+ /* lightSensorRate= */ anyInt(),
+ /* initialLightSensorRate= */ anyInt(),
+ /* brighteningLightDebounceConfig */ anyLong(),
+ /* darkeningLightDebounceConfig */ anyLong(),
+ /* brighteningLightDebounceConfigIdle= */ anyLong(),
+ /* darkeningLightDebounceConfigIdle= */ anyLong(),
+ /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1179,9 +1180,9 @@ public final class DisplayPowerController2Test {
eq(mContext),
any(BrightnessRangeController.class),
any(BrightnessThrottler.class),
- isNull(),
- anyInt(),
- anyInt(),
+ /* idleModeBrightnessMapper= */ isNull(),
+ /* ambientLightHorizonShort= */ anyInt(),
+ /* ambientLightHorizonLong= */ anyInt(),
eq(lux),
eq(brightness)
);
@@ -1293,9 +1294,8 @@ public final class DisplayPowerController2Test {
public void testRampRateForHdrContent_HdrClamperOn() {
float clampedBrightness = 0.6f;
float transitionRate = 1.5f;
- DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
- when(flags.isHdrClamperEnabled()).thenReturn(true);
- mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+ when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
DisplayPowerRequest dpr = new DisplayPowerRequest();
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
@@ -1391,9 +1391,8 @@ public final class DisplayPowerController2Test {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
- DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
- when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
- mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+ when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
// Send a display power request
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1446,9 +1445,8 @@ public final class DisplayPowerController2Test {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
public void testRampMaxTimeIdle_DifferentValues() {
- DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
- when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
- mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+ when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
// Send a display power request
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1480,8 +1478,6 @@ public final class DisplayPowerController2Test {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with DOZE.
@@ -1508,8 +1504,6 @@ public final class DisplayPowerController2Test {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with DOZE.
@@ -1535,8 +1529,6 @@ public final class DisplayPowerController2Test {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with OFF.
@@ -1552,26 +1544,29 @@ public final class DisplayPowerController2Test {
verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
}
- private void initDisplayOffloadSession() {
- mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
- @Override
- public boolean startOffload() {
- return true;
- }
-
- @Override
- public void stopOffload() {}
- });
-
- mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
- @Override
- public void setDozeStateOverride(int displayState) {}
-
- @Override
- public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
- return mDisplayOffloader;
- }
- };
+ @Test
+ public void testBrightnessFromOffload() {
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ float brightness = 0.34f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ mHolder.dpc.setBrightnessFromOffload(brightness);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // One triggered by handleBrightnessModeChange, another triggered by
+ // setBrightnessFromOffload
+ verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
/**
@@ -1618,23 +1613,13 @@ public final class DisplayPowerController2Test {
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
when(displayDeviceConfigMock.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_PROXIMITY;
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_PROXIMITY, null));
when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true);
when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn(
- new DisplayDeviceConfig.SensorData());
+ new SensorData());
when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_LIGHT;
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_LIGHT, null));
when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
.thenReturn(new int[0]);
@@ -1668,12 +1653,6 @@ public final class DisplayPowerController2Test {
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId, boolean isEnabled) {
- return createDisplayPowerController(displayId, uniqueId, isEnabled,
- mock(DisplayManagerFlags.class));
- }
-
- private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
- String uniqueId, boolean isEnabled, DisplayManagerFlags flags) {
final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
final AutomaticBrightnessController automaticBrightnessController =
@@ -1699,7 +1678,7 @@ public final class DisplayPowerController2Test {
TestInjector injector = spy(new TestInjector(displayPowerState, animator,
automaticBrightnessController, wakelockController, brightnessMappingStrategy,
hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
- clamperController, flags));
+ clamperController, mDisplayManagerFlagsMock));
final LogicalDisplay display = mock(LogicalDisplay.class);
final DisplayDevice device = mock(DisplayDevice.class);
@@ -1714,7 +1693,7 @@ public final class DisplayPowerController2Test {
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {
},
- hbmMetadata, /* bootCompleted= */ false, flags);
+ hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock);
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, wakelockController,
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 37ee23f38b14..b22799377872 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -76,6 +76,7 @@ import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.feature.flags.Flags;
import com.android.server.display.layout.Layout;
@@ -124,8 +125,6 @@ public final class DisplayPowerControllerTest {
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
- private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
- private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -145,6 +144,8 @@ public final class DisplayPowerControllerTest {
private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
@Mock
private DisplayManagerFlags mDisplayManagerFlagsMock;
+ @Mock
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
@@ -1093,19 +1094,19 @@ public final class DisplayPowerControllerTest {
any(AutomaticBrightnessController.Callbacks.class),
any(Looper.class),
eq(mSensorManagerMock),
- any(),
+ /* lightSensor= */ any(),
eq(mHolder.brightnessMappingStrategy),
- anyInt(),
- anyFloat(),
- anyFloat(),
- anyFloat(),
- anyInt(),
- anyInt(),
- anyLong(),
- anyLong(),
- anyLong(),
- anyLong(),
- anyBoolean(),
+ /* lightSensorWarmUpTime= */ anyInt(),
+ /* brightnessMin= */ anyFloat(),
+ /* brightnessMax= */ anyFloat(),
+ /* dozeScaleFactor */ anyFloat(),
+ /* lightSensorRate= */ anyInt(),
+ /* initialLightSensorRate= */ anyInt(),
+ /* brighteningLightDebounceConfig */ anyLong(),
+ /* darkeningLightDebounceConfig */ anyLong(),
+ /* brighteningLightDebounceConfigIdle= */ anyLong(),
+ /* darkeningLightDebounceConfigIdle= */ anyLong(),
+ /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1113,9 +1114,9 @@ public final class DisplayPowerControllerTest {
eq(mContext),
any(BrightnessRangeController.class),
any(BrightnessThrottler.class),
- isNull(),
- anyInt(),
- anyInt(),
+ /* idleModeBrightnessMapper= */ isNull(),
+ /* ambientLightHorizonShort= */ anyInt(),
+ /* ambientLightHorizonLong= */ anyInt(),
eq(lux),
eq(brightness)
);
@@ -1385,8 +1386,6 @@ public final class DisplayPowerControllerTest {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with DOZE.
@@ -1413,8 +1412,6 @@ public final class DisplayPowerControllerTest {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with DOZE.
@@ -1440,8 +1437,6 @@ public final class DisplayPowerControllerTest {
when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
return null;
}).when(mHolder.displayPowerState).setScreenState(anyInt());
- // init displayoffload session and support offloading.
- initDisplayOffloadSession();
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
// start with OFF.
@@ -1457,28 +1452,6 @@ public final class DisplayPowerControllerTest {
verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
}
- private void initDisplayOffloadSession() {
- mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
- @Override
- public boolean startOffload() {
- return true;
- }
-
- @Override
- public void stopOffload() {}
- });
-
- mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
- @Override
- public void setDozeStateOverride(int displayState) {}
-
- @Override
- public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
- return mDisplayOffloader;
- }
- };
- }
-
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -1515,23 +1488,13 @@ public final class DisplayPowerControllerTest {
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
when(displayDeviceConfigMock.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_PROXIMITY;
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_PROXIMITY, null));
when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true);
when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn(
- new DisplayDeviceConfig.SensorData());
+ new SensorData());
when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_LIGHT;
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_LIGHT, null));
when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
.thenReturn(new int[0]);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 534a708af3c7..ebd6614aba14 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -37,6 +37,7 @@ import android.view.Display;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.config.SensorData;
import com.android.server.testutils.OffsettableClock;
import org.junit.Before;
@@ -74,14 +75,7 @@ public final class DisplayPowerProximityStateControllerTest {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_PROXIMITY;
- // This is kept null because currently there is no way to define a sensor
- // name in TestUtils
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_PROXIMITY, null));
setUpProxSensor();
DisplayPowerProximityStateController.Injector injector =
new DisplayPowerProximityStateController.Injector() {
@@ -171,13 +165,7 @@ public final class DisplayPowerProximityStateControllerTest {
@Test
public void isProximitySensorAvailableReturnsFalseWhenNotAvailableAndNoDefault() {
- when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = null;
- name = null;
- }
- });
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(new SensorData());
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
@@ -188,13 +176,7 @@ public final class DisplayPowerProximityStateControllerTest {
@Test
public void isProximitySensorAvailableReturnsTrueWhenNotAvailableAndHasDefault()
throws Exception {
- when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = null;
- name = null;
- }
- });
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(new SensorData());
when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
@@ -207,13 +189,7 @@ public final class DisplayPowerProximityStateControllerTest {
@Test
public void isProximitySensorAvailableReturnsFalseWhenNotAvailableHasDefaultNonDefaultDisplay()
throws Exception {
- when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = null;
- name = null;
- }
- });
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(new SensorData());
when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
@@ -240,12 +216,7 @@ public final class DisplayPowerProximityStateControllerTest {
public void notifyDisplayDeviceChangedReloadsTheProximitySensor() throws Exception {
DisplayDeviceConfig updatedDisplayDeviceConfig = mock(DisplayDeviceConfig.class);
when(updatedDisplayDeviceConfig.getProximitySensor()).thenReturn(
- new DisplayDeviceConfig.SensorData() {
- {
- type = Sensor.STRING_TYPE_PROXIMITY;
- name = null;
- }
- });
+ new SensorData(Sensor.STRING_TYPE_PROXIMITY, null));
Sensor newProxSensor = TestUtils.createSensor(
Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 4.0f);
when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
new file mode 100644
index 000000000000..49fa2545e001
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display
+
+import android.view.Display
+import androidx.test.filters.SmallTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.junit.MockitoJUnit
+
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+class DisplayPowerStateTest {
+
+ private lateinit var displayPowerState: DisplayPowerState
+
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ private val mockBlanker = mock<DisplayBlanker>()
+ private val mockColorFade = mock<ColorFade>()
+
+ @Before
+ fun setUp() {
+ displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON)
+ }
+
+ @Test
+ fun `destroys ColorFade on stop`() {
+ displayPowerState.stop()
+
+ verify(mockColorFade).destroy()
+ }
+} \ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
new file mode 100644
index 000000000000..fea431c5623a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -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.server.display;
+
+import static android.hardware.display.DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED;
+import static android.view.Display.TYPE_EXTERNAL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+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.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.RemoteException;
+import android.os.Temperature;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.notifications.DisplayNotificationManager;
+import com.android.server.testutils.TestHandler;
+
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
+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;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+
+/**
+ * Tests for {@link ExternalDisplayPolicy}
+ * Run: atest ExternalDisplayPolicyTest
+ */
+@SmallTest
+@RunWith(TestParameterInjector.class)
+public class ExternalDisplayPolicyTest {
+ private static final int EXTERNAL_DISPLAY_ID = 1;
+ private static final Temperature MODERATE_TEMPERATURE = new Temperature(/*value=*/ 40.5f,
+ /*type=*/ Temperature.TYPE_SKIN,
+ /*name=*/ "Test",
+ /*status=*/ Temperature.THROTTLING_MODERATE);
+ private static final Temperature SEVERE_TEMPERATURE = new Temperature(/*value=*/ 50.5f,
+ /*type=*/ Temperature.TYPE_SKIN,
+ /*name=*/ "Test",
+ /*status=*/ Temperature.THROTTLING_SEVERE);
+ private static final Temperature CRITICAL_TEMPERATURE = new Temperature(/*value=*/ 70.5f,
+ /*type=*/ Temperature.TYPE_SKIN,
+ /*name=*/ "Test",
+ /*status=*/ Temperature.THROTTLING_CRITICAL);
+ private static final Temperature EMERGENCY_TEMPERATURE = new Temperature(/*value=*/ 80.5f,
+ /*type=*/ Temperature.TYPE_SKIN,
+ /*name=*/ "Test",
+ /*status=*/ Temperature.THROTTLING_EMERGENCY);
+ @Mock
+ private ExternalDisplayPolicy.Injector mMockedInjector;
+ @Mock
+ private DisplayManagerFlags mMockedFlags;
+ @Mock
+ private LogicalDisplayMapper mMockedLogicalDisplayMapper;
+ @Mock
+ private IThermalService mMockedThermalService;
+ @Mock
+ private SyncRoot mMockedSyncRoot;
+ @Mock
+ private LogicalDisplay mMockedLogicalDisplay;
+ @Mock
+ private DisplayNotificationManager mMockedDisplayNotificationManager;
+ @Captor
+ private ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mThermalEventTypeCaptor;
+ @Captor
+ private ArgumentCaptor<Consumer<LogicalDisplay>> mLogicalDisplayConsumerCaptor;
+ @Captor
+ private ArgumentCaptor<Boolean> mIsEnabledCaptor;
+ @Captor
+ private ArgumentCaptor<LogicalDisplay> mLogicalDisplayCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mDisplayEventCaptor;
+ private ExternalDisplayPolicy mExternalDisplayPolicy;
+ private TestHandler mHandler;
+
+ /** Setup tests. */
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new TestHandler(/*callback=*/ null);
+ when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled()).thenReturn(true);
+ when(mMockedInjector.getFlags()).thenReturn(mMockedFlags);
+ when(mMockedInjector.getLogicalDisplayMapper()).thenReturn(mMockedLogicalDisplayMapper);
+ when(mMockedInjector.getThermalService()).thenReturn(mMockedThermalService);
+ when(mMockedInjector.getSyncRoot()).thenReturn(mMockedSyncRoot);
+ when(mMockedInjector.getDisplayNotificationManager()).thenReturn(
+ mMockedDisplayNotificationManager);
+ when(mMockedInjector.getHandler()).thenReturn(mHandler);
+ mExternalDisplayPolicy = new ExternalDisplayPolicy(mMockedInjector);
+
+ // Initialize mocked logical display
+ when(mMockedLogicalDisplay.getDisplayIdLocked()).thenReturn(EXTERNAL_DISPLAY_ID);
+ when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(true);
+ final var mockedLogicalDisplayInfo = new DisplayInfo();
+ mockedLogicalDisplayInfo.type = TYPE_EXTERNAL;
+ when(mMockedLogicalDisplay.getDisplayInfoLocked()).thenReturn(mockedLogicalDisplayInfo);
+ when(mMockedLogicalDisplayMapper.getDisplayLocked(EXTERNAL_DISPLAY_ID)).thenReturn(
+ mMockedLogicalDisplay);
+ }
+
+ @Test
+ public void testTryEnableExternalDisplay_criticalThermalCondition() throws RemoteException {
+ // Disallow external displays due to thermals.
+ setTemperature(registerThermalListener(), List.of(CRITICAL_TEMPERATURE));
+ assertIsExternalDisplayAllowed(/*enabled=*/ false);
+ assertDisplaySetEnabled(/*enabled=*/ false);
+
+ // Check that display can not be enabled with tryEnableExternalDisplay.
+ mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay,
+ /*enabled=*/ true);
+ mHandler.flush();
+ verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean());
+ verify(mMockedDisplayNotificationManager, times(2))
+ .onHighTemperatureExternalDisplayNotAllowed();
+ }
+
+ @Test
+ public void testTryEnableExternalDisplay_featureDisabled(@TestParameter final boolean enable) {
+ when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
+ mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay, enable);
+ mHandler.flush();
+ verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean());
+ verify(mMockedDisplayNotificationManager, never())
+ .onHighTemperatureExternalDisplayNotAllowed();
+ }
+
+ @Test
+ public void testTryDisableExternalDisplay_criticalThermalCondition() throws RemoteException {
+ // Disallow external displays due to thermals.
+ setTemperature(registerThermalListener(), List.of(CRITICAL_TEMPERATURE));
+ assertIsExternalDisplayAllowed(/*enabled=*/ false);
+ assertDisplaySetEnabled(/*enabled=*/ false);
+
+ // Check that display can be disabled with tryEnableExternalDisplay.
+ mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay,
+ /*enabled=*/ false);
+ mHandler.flush();
+ assertDisplaySetEnabled(/*enabled=*/ false);
+ // Expected only 1 invocation, upon critical temperature.
+ verify(mMockedDisplayNotificationManager).onHighTemperatureExternalDisplayNotAllowed();
+ }
+
+ @Test
+ public void testSetEnabledExternalDisplay(@TestParameter final boolean enable) {
+ mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay, enable);
+ assertDisplaySetEnabled(enable);
+ }
+
+ @Test
+ public void testOnExternalDisplayAvailable() {
+ when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false);
+ mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay);
+ assertAskedToEnableDisplay();
+ }
+
+ @Test
+ public void testOnExternalDisplayAvailable_criticalThermalCondition()
+ throws RemoteException {
+ // Disallow external displays due to thermals.
+ setTemperature(registerThermalListener(), List.of(CRITICAL_TEMPERATURE));
+ assertIsExternalDisplayAllowed(/*enabled=*/ false);
+ assertDisplaySetEnabled(/*enabled=*/ false);
+
+ when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false);
+ mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay);
+ verify(mMockedInjector, never()).sendExternalDisplayEventLocked(any(), anyInt());
+ verify(mMockedDisplayNotificationManager, times(2))
+ .onHighTemperatureExternalDisplayNotAllowed();
+ }
+
+ @Test
+ public void testNoThermalListenerRegistered_featureDisabled(
+ @TestParameter final boolean isConnectedDisplayManagementEnabled,
+ @TestParameter final boolean isErrorHandlingEnabled) throws RemoteException {
+ assumeFalse(isConnectedDisplayManagementEnabled && isErrorHandlingEnabled);
+ when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(
+ isConnectedDisplayManagementEnabled);
+ when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled()).thenReturn(
+ isErrorHandlingEnabled);
+
+ mExternalDisplayPolicy.onBootCompleted();
+ verify(mMockedThermalService, never()).registerThermalEventListenerWithType(
+ any(), anyInt());
+ }
+
+ @Test
+ public void testOnCriticalTemperature_disallowAndAllowExternalDisplay() throws RemoteException {
+ final var thermalListener = registerThermalListener();
+
+ setTemperature(thermalListener, List.of(CRITICAL_TEMPERATURE, EMERGENCY_TEMPERATURE));
+ assertIsExternalDisplayAllowed(/*enabled=*/ false);
+ assertDisplaySetEnabled(false);
+
+ thermalListener.notifyThrottling(SEVERE_TEMPERATURE);
+ thermalListener.notifyThrottling(MODERATE_TEMPERATURE);
+ assertIsExternalDisplayAllowed(/*enabled=*/ true);
+ verify(mMockedLogicalDisplayMapper, never()).forEachLocked(any());
+ }
+
+ private void setTemperature(final IThermalEventListener thermalEventListener,
+ final List<Temperature> temperature) throws RemoteException {
+ for (var t : temperature) {
+ thermalEventListener.notifyThrottling(t);
+ }
+ verify(mMockedLogicalDisplayMapper).forEachLocked(mLogicalDisplayConsumerCaptor.capture());
+ mLogicalDisplayConsumerCaptor.getValue().accept(mMockedLogicalDisplay);
+ }
+
+ private void assertDisplaySetEnabled(final boolean enabled) {
+ // Check setDisplayEnabledLocked is triggered to disable display.
+ verify(mMockedLogicalDisplayMapper).setDisplayEnabledLocked(
+ mLogicalDisplayCaptor.capture(), mIsEnabledCaptor.capture());
+ assertThat(mLogicalDisplayCaptor.getValue()).isEqualTo(mMockedLogicalDisplay);
+ assertThat(mIsEnabledCaptor.getValue()).isEqualTo(enabled);
+ clearInvocations(mMockedLogicalDisplayMapper);
+ when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(enabled);
+ }
+
+ private void assertAskedToEnableDisplay() {
+ // Check sendExternalDisplayEventLocked is triggered when display can be enabled.
+ verify(mMockedInjector).sendExternalDisplayEventLocked(mLogicalDisplayCaptor.capture(),
+ mDisplayEventCaptor.capture());
+ assertThat(mLogicalDisplayCaptor.getValue()).isEqualTo(mMockedLogicalDisplay);
+ assertThat(mDisplayEventCaptor.getValue()).isEqualTo(EVENT_DISPLAY_CONNECTED);
+ clearInvocations(mMockedLogicalDisplayMapper);
+ when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(true);
+ }
+
+ private void assertIsExternalDisplayAllowed(final boolean enabled) {
+ assertThat(mExternalDisplayPolicy.isExternalDisplayAllowed()).isEqualTo(enabled);
+ }
+
+ private IThermalEventListener registerThermalListener() throws RemoteException {
+ // Initialize and register thermal listener
+ mExternalDisplayPolicy.onBootCompleted();
+ verify(mMockedThermalService).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), mThermalEventTypeCaptor.capture());
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+ assertThat(listener).isNotNull();
+ assertThat(mThermalEventTypeCaptor.getValue()).isEqualTo(Temperature.TYPE_SKIN);
+ return listener;
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index a77a9586fb43..8270845657c6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
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.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -40,12 +41,12 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.PowerManager;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.SurfaceControl;
@@ -118,10 +119,12 @@ public class LocalDisplayAdapterTest {
private DisplayNotificationManager mMockedDisplayNotificationManager;
@Mock
private DisplayManagerFlags mFlags;
+ @Mock
+ private DisplayPowerControllerInterface mMockedDisplayPowerController;
private Handler mHandler;
- private DisplayOffloadSession mDisplayOffloadSession;
+ private DisplayOffloadSessionImpl mDisplayOffloadSession;
private DisplayOffloader mDisplayOffloader;
@@ -1195,6 +1198,7 @@ public class LocalDisplayAdapterTest {
}
verify(mDisplayOffloader, times(mDisplayOffloadSupportedStates.size())).startOffload();
+ assertTrue(mDisplayOffloadSession.isActive());
}
@Test
@@ -1217,6 +1221,9 @@ public class LocalDisplayAdapterTest {
changeStateToDozeRunnable.run();
verify(mDisplayOffloader).stopOffload();
+ assertFalse(mDisplayOffloadSession.isActive());
+ verify(mMockedDisplayPowerController).setBrightnessFromOffload(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT);
}
private void initDisplayOffloadSession() {
@@ -1230,15 +1237,8 @@ public class LocalDisplayAdapterTest {
public void stopOffload() {}
});
- mDisplayOffloadSession = new DisplayOffloadSession() {
- @Override
- public void setDozeStateOverride(int displayState) {}
-
- @Override
- public DisplayOffloader getDisplayOffloader() {
- return mDisplayOffloader;
- }
- };
+ mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
+ mMockedDisplayPowerController);
}
private void setupCutoutAndRoundedCorners() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 8b13018fc14b..43d2b8f741ae 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -346,14 +346,14 @@ public class LogicalDisplayMapperTest {
// Disable device
mLogicalDisplayMapper.setDisplayEnabledLocked(
- displayAdded.getDisplayIdLocked(), /* isEnabled= */ false);
+ displayAdded, /* isEnabled= */ false);
verify(mListenerMock).onLogicalDisplayEventLocked(mDisplayCaptor.capture(),
eq(LOGICAL_DISPLAY_EVENT_REMOVED));
clearInvocations(mListenerMock);
// Enable device
mLogicalDisplayMapper.setDisplayEnabledLocked(
- displayAdded.getDisplayIdLocked(), /* isEnabled= */ true);
+ displayAdded, /* isEnabled= */ true);
verify(mListenerMock).onLogicalDisplayEventLocked(mDisplayCaptor.capture(),
eq(LOGICAL_DISPLAY_EVENT_ADDED));
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 8bbacc494efd..73e7ba0750e0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -77,7 +77,7 @@ public class VirtualDisplayAdapterTest {
DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
/* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
- /* surface= */ null, /* flags= */ 0, config);
+ /* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config);
assertNotNull(result);
}
@@ -89,12 +89,12 @@ public class VirtualDisplayAdapterTest {
VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null,
- /* ownerUid= */ 10, /* packageName= */ "testpackage", /* surface= */ null,
- /* flags= */ 0, config1);
+ /* ownerUid= */ 10, /* packageName= */ "testpackage", /* uniqueId= */ "uniqueId1",
+ /* surface= */ null, /* flags= */ 0, config1);
DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
/* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
- /* surface= */ null, /* flags= */ 0, config2);
+ /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2);
assertNull(result);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c4f483810478..52fa91f5fe0e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -42,7 +42,9 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.display.AutomaticBrightnessController;
import com.android.server.display.BrightnessSetting;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.Before;
import org.junit.Test;
@@ -66,6 +68,8 @@ public final class DisplayBrightnessControllerTest {
private BrightnessSetting mBrightnessSetting;
@Mock
private Runnable mOnBrightnessChangeRunnable;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
@Mock
private HandlerExecutor mBrightnessChangeExecutor;
@@ -74,7 +78,7 @@ public final class DisplayBrightnessControllerTest {
DisplayBrightnessController.Injector() {
@Override
DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(
- Context context, int displayId) {
+ Context context, int displayId, DisplayManagerFlags flags) {
return mDisplayBrightnessStrategySelector;
}
};
@@ -92,7 +96,7 @@ public final class DisplayBrightnessControllerTest {
.thenReturn(true);
mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable,
- mBrightnessChangeExecutor);
+ mBrightnessChangeExecutor, mDisplayManagerFlags);
}
@Test
@@ -350,7 +354,7 @@ public final class DisplayBrightnessControllerTest {
int nonDefaultDisplayId = 1;
mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
nonDefaultDisplayId, DEFAULT_BRIGHTNESS, mBrightnessSetting,
- mOnBrightnessChangeRunnable, mBrightnessChangeExecutor);
+ mOnBrightnessChangeRunnable, mBrightnessChangeExecutor, mDisplayManagerFlags);
brightness = 0.5f;
when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
mDisplayBrightnessController.setAutomaticBrightnessController(
@@ -384,4 +388,14 @@ public final class DisplayBrightnessControllerTest {
verify(mBrightnessSetting).setBrightness(brightnessValue2);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
}
+
+ @Test
+ public void setBrightnessFromOffload() {
+ float brightness = 0.4f;
+ OffloadBrightnessStrategy offloadBrightnessStrategy = mock(OffloadBrightnessStrategy.class);
+ when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn(
+ offloadBrightnessStrategy);
+ mDisplayBrightnessController.setBrightnessFromOffload(brightness);
+ verify(offloadBrightnessStrategy).setOffloadScreenBrightness(brightness);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 8497dabba67d..37958faed1ca 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -35,13 +36,16 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
+import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.Before;
import org.junit.Rule;
@@ -71,10 +75,64 @@ public final class DisplayBrightnessStrategySelectorTest {
@Mock
private FollowerBrightnessStrategy mFollowerBrightnessStrategy;
@Mock
+ private AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
+ @Mock
+ private OffloadBrightnessStrategy mOffloadBrightnessStrategy;
+ @Mock
private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
private Context mContext;
+ private DisplayBrightnessStrategySelector.Injector mInjector =
+ new DisplayBrightnessStrategySelector.Injector() {
+ @Override
+ ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() {
+ return mScreenOffBrightnessModeStrategy;
+ }
+
+ @Override
+ DozeBrightnessStrategy getDozeBrightnessStrategy() {
+ return mDozeBrightnessModeStrategy;
+ }
+
+ @Override
+ OverrideBrightnessStrategy getOverrideBrightnessStrategy() {
+ return mOverrideBrightnessStrategy;
+ }
+
+ @Override
+ TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() {
+ return mTemporaryBrightnessStrategy;
+ }
+
+ @Override
+ BoostBrightnessStrategy getBoostBrightnessStrategy() {
+ return mBoostBrightnessStrategy;
+ }
+
+ @Override
+ FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
+ return mFollowerBrightnessStrategy;
+ }
+
+ @Override
+ InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
+ return mInvalidBrightnessStrategy;
+ }
+
+ @Override
+ AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context,
+ int displayId) {
+ return mAutomaticBrightnessStrategy;
+ }
+
+ @Override
+ OffloadBrightnessStrategy getOffloadBrightnessStrategy() {
+ return mOffloadBrightnessStrategy;
+ }
+ };
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -87,45 +145,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mContext.getContentResolver()).thenReturn(contentResolver);
when(mContext.getResources()).thenReturn(mResources);
when(mInvalidBrightnessStrategy.getName()).thenReturn("InvalidBrightnessStrategy");
- DisplayBrightnessStrategySelector.Injector injector =
- new DisplayBrightnessStrategySelector.Injector() {
- @Override
- ScreenOffBrightnessStrategy getScreenOffBrightnessStrategy() {
- return mScreenOffBrightnessModeStrategy;
- }
-
- @Override
- DozeBrightnessStrategy getDozeBrightnessStrategy() {
- return mDozeBrightnessModeStrategy;
- }
-
- @Override
- OverrideBrightnessStrategy getOverrideBrightnessStrategy() {
- return mOverrideBrightnessStrategy;
- }
-
- @Override
- TemporaryBrightnessStrategy getTemporaryBrightnessStrategy() {
- return mTemporaryBrightnessStrategy;
- }
-
- @Override
- BoostBrightnessStrategy getBoostBrightnessStrategy() {
- return mBoostBrightnessStrategy;
- }
-
- @Override
- FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
- return mFollowerBrightnessStrategy;
- }
-
- @Override
- InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
- return mInvalidBrightnessStrategy;
- }
- };
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
- injector, DISPLAY_ID);
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
}
@@ -188,6 +209,7 @@ public final class DisplayBrightnessStrategySelectorTest {
displayPowerRequest.screenBrightnessOverride = Float.NaN;
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON), mInvalidBrightnessStrategy);
}
@@ -200,4 +222,35 @@ public final class DisplayBrightnessStrategySelectorTest {
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON), mFollowerBrightnessStrategy);
}
+
+ @Test
+ public void selectStrategySelectsOffloadStrategyWhenValid() {
+ when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
+ assertEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy(
+ displayPowerRequest, Display.STATE_ON));
+ }
+
+ @Test
+ public void selectStrategyDoesNotSelectOffloadStrategyWhenFeatureFlagDisabled() {
+ when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(false);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
+ assertNotEquals(mOffloadBrightnessStrategy,
+ mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
+ Display.STATE_ON));
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index b652576a75c8..78ec2ff31161 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -188,6 +188,29 @@ public class AutomaticBrightnessStrategyTest {
}
@Test
+ public void testAutoBrightnessState_BrightnessReasonIsOffload() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_ON;
+ boolean allowAutoBrightnessWhileDozing = false;
+ int brightnessReason = BrightnessReason.REASON_OFFLOAD;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true);
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController)
+ .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
+ mBrightnessConfiguration,
+ lastUserSetBrightness,
+ userSetBrightnessChanged, 0.5f,
+ false, policy, true);
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+ }
+
+ @Test
public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_DOZE;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
new file mode 100644
index 000000000000..36719af10abb
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OffloadBrightnessStrategyTest {
+
+ private OffloadBrightnessStrategy mOffloadBrightnessStrategy;
+
+ @Before
+ public void before() {
+ mOffloadBrightnessStrategy = new OffloadBrightnessStrategy();
+ }
+
+ @Test
+ public void testUpdateBrightnessWhenOffloadBrightnessIsSet() {
+ DisplayManagerInternal.DisplayPowerRequest
+ displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+ float brightness = 0.2f;
+ mOffloadBrightnessStrategy.setOffloadScreenBrightness(brightness);
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_OFFLOAD);
+ DisplayBrightnessState expectedDisplayBrightnessState =
+ new DisplayBrightnessState.Builder()
+ .setBrightness(brightness)
+ .setBrightnessReason(brightnessReason)
+ .setSdrBrightness(brightness)
+ .setDisplayBrightnessStrategyName(mOffloadBrightnessStrategy.getName())
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .build();
+ DisplayBrightnessState updatedDisplayBrightnessState =
+ mOffloadBrightnessStrategy.updateBrightness(displayPowerRequest);
+ assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 6a95d5c57024..499e7008febd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -229,7 +229,19 @@ public class DisplayModeDirectorTest {
LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight(),
0, APP_MODE_65.getRefreshRate())),
- /*displayResolutionRangeVotingEnabled*/ true}});
+ /*displayResolutionRangeVotingEnabled*/ true},
+ {/*expectedBaseModeId*/ APP_MODE_65.getModeId(),
+ /*expectedPhysicalRefreshRate*/ 64.99f,
+ /*expectedAppRequestedRefreshRate*/ 64.99f,
+ /*votesWithPriorities*/ Map.of(
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forPhysicalRefreshRates(
+ 0, 64.99f))}});
final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
index 1d2034be4acb..4efd15c49bb5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
@@ -75,6 +75,14 @@ public class DisplayNotificationManagerTest {
}
@Test
+ public void testNotificationOnHighTemperatureExternalDisplayNotAllowed() {
+ var dnm = createDisplayNotificationManager(/*isNotificationManagerAvailable=*/ true,
+ /*isErrorHandlingEnabled=*/ true);
+ dnm.onHighTemperatureExternalDisplayNotAllowed();
+ assertExpectedNotification();
+ }
+
+ @Test
public void testNotificationOnHotplugConnectionError() {
var dnm = createDisplayNotificationManager(/*isNotificationManagerAvailable=*/ true,
/*isErrorHandlingEnabled=*/ true);
@@ -112,6 +120,7 @@ public class DisplayNotificationManagerTest {
dnm.onHotplugConnectionError();
dnm.onDisplayPortLinkTrainingFailure();
dnm.onCableNotCapableDisplayPort();
+ dnm.onHighTemperatureExternalDisplayNotAllowed();
verify(mMockedNotificationManager, never()).notify(anyString(), anyInt(), any());
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java
index 4494b0c412dc..6e2d954c05a8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java
@@ -28,7 +28,7 @@ import android.hardware.input.InputSensorInfo;
import androidx.test.filters.SmallTest;
import com.android.internal.annotations.Keep;
-import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.config.SensorData;
import org.junit.Before;
import org.junit.Test;
@@ -123,9 +123,7 @@ public class SensorUtilsTest {
when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(allSensors);
when(mSensorManager.getDefaultSensor(fallbackType)).thenReturn(defaultSensor);
- SensorData sensorData = new SensorData();
- sensorData.name = sensorName;
- sensorData.type = sensorType;
+ SensorData sensorData = new SensorData(sensorType, sensorName);
Sensor result = SensorUtils.findSensor(mSensorManager, sensorData, fallbackType);
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 063af573e1f3..113511ef8197 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@ android_test {
"androidx.test.espresso.core",
"androidx.test.espresso.contrib",
"androidx.test.ext.truth",
+ "backup_flags_lib",
"flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
new file mode 100644
index 000000000000..72dc7259dc1f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.annotation.NonNull;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.TestLooperManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.SparseArray;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Rule;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class BaseBroadcastQueueTest {
+
+ static final int USER_GUEST = 11;
+
+ static final String PACKAGE_ANDROID = "android";
+ static final String PACKAGE_PHONE = "com.android.phone";
+ static final String PACKAGE_RED = "com.example.red";
+ static final String PACKAGE_GREEN = "com.example.green";
+ static final String PACKAGE_BLUE = "com.example.blue";
+ static final String PACKAGE_YELLOW = "com.example.yellow";
+ static final String PACKAGE_ORANGE = "com.example.orange";
+
+ static final String PROCESS_SYSTEM = "system";
+
+ static final String CLASS_RED = "com.example.red.Red";
+ static final String CLASS_GREEN = "com.example.green.Green";
+ static final String CLASS_BLUE = "com.example.blue.Blue";
+ static final String CLASS_YELLOW = "com.example.yellow.Yellow";
+ static final String CLASS_ORANGE = "com.example.orange.Orange";
+
+ static final BroadcastProcessQueue.BroadcastPredicate BROADCAST_PREDICATE_ANY =
+ (r, i) -> true;
+
+ @Rule
+ public final ApplicationExitInfoTest.ServiceThreadRule
+ mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+ @Mock
+ AppOpsService mAppOpsService;
+ @Mock
+ PackageManagerInternal mPackageManagerInt;
+ @Mock
+ UsageStatsManagerInternal mUsageStatsManagerInt;
+ @Mock
+ DropBoxManagerInternal mDropBoxManagerInt;
+ @Mock
+ AlarmManagerInternal mAlarmManagerInt;
+ @Mock
+ ProcessList mProcessList;
+
+ Context mContext;
+ ActivityManagerService mAms;
+ BroadcastConstants mConstants;
+ BroadcastSkipPolicy mSkipPolicy;
+ HandlerThread mHandlerThread;
+ TestLooperManager mLooper;
+ AtomicInteger mNextPid;
+
+ /**
+ * Map from PID to registered registered runtime receivers.
+ */
+ SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mHandlerThread = new HandlerThread(getTag());
+ mHandlerThread.start();
+ // Pause all event processing until a test chooses to resume
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+ mNextPid = new AtomicInteger(100);
+
+ LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+ LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ LocalServices.removeServiceForTest(AlarmManagerInternal.class);
+ LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
+ doAnswer((invocation) -> {
+ return getUidForPackage(invocation.getArgument(0));
+ }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
+
+ final ActivityManagerService realAms = new ActivityManagerService(
+ new TestInjector(mContext), mServiceThreadRule.getThread());
+ realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+ realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
+ realAms.mPackageManagerInt = mPackageManagerInt;
+ realAms.mUsageStatsService = mUsageStatsManagerInt;
+ realAms.mProcessesReady = true;
+ mAms = spy(realAms);
+
+ mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+ doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+ doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
+
+ mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+ }
+
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ static int getUidForPackage(@NonNull String packageName) {
+ switch (packageName) {
+ case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
+ case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
+ case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
+ case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
+ case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+ case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
+ case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ static int getUidForPackage(@NonNull String packageName, int userId) {
+ return UserHandle.getUid(userId, getUidForPackage(packageName));
+ }
+
+ private class TestInjector extends ActivityManagerService.Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+ Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
+ }
+
+ abstract String getTag();
+
+ static ApplicationInfo makeApplicationInfo(String packageName) {
+ return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
+ }
+
+ static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.processName = processName;
+ ai.uid = getUidForPackage(packageName, userId);
+ return ai;
+ }
+
+ static ResolveInfo withPriority(ResolveInfo info, int priority) {
+ info.priority = priority;
+ return info;
+ }
+
+ static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
+ filter.setPriority(priority);
+ return filter;
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name) {
+ return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
+ return makeManifestReceiver(packageName, packageName, name, userId);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String processName,
+ String name, int userId) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.processName = processName;
+ ri.activityInfo.name = name;
+ ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
+ return ri;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
+ return makeRegisteredReceiver(app, 0);
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
+ static BroadcastFilter makeRegisteredReceiver(ReceiverList receiverList, int priority) {
+ final IntentFilter filter = new IntentFilter();
+ filter.setPriority(priority);
+ final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
+ receiverList.app.info.packageName, null, null, null, receiverList.uid,
+ receiverList.userId, false, false, true);
+ receiverList.add(res);
+ return res;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 08f5d03e0148..2378416f8bd0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -31,17 +31,6 @@ import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ORDERE
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
-import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
-import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
-import static com.android.server.am.BroadcastQueueTest.CLASS_RED;
-import static com.android.server.am.BroadcastQueueTest.CLASS_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_BLUE;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_GREEN;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_RED;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
-import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
-import static com.android.server.am.BroadcastQueueTest.withPriority;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
import static com.google.common.truth.Truth.assertThat;
@@ -74,17 +63,15 @@ import android.appwidget.AppWidgetManager;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.BundleMerger;
import android.os.DropBoxManager;
-import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -108,11 +95,12 @@ import java.util.List;
import java.util.Objects;
@SmallTest
-public final class BroadcastQueueModernImplTest {
+public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
+ private static final String TAG = "BroadcastQueueModernImplTest";
+
private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
- @Mock ActivityManagerService mAms;
@Mock ProcessRecord mProcess;
@Mock BroadcastProcessQueue mQueue1;
@@ -120,11 +108,6 @@ public final class BroadcastQueueModernImplTest {
@Mock BroadcastProcessQueue mQueue3;
@Mock BroadcastProcessQueue mQueue4;
- HandlerThread mHandlerThread;
- TestLooperManager mLooper;
-
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
BroadcastQueueModernImpl mImpl;
BroadcastProcessQueue mHead;
@@ -136,22 +119,12 @@ public final class BroadcastQueueModernImplTest {
@Before
public void setUp() throws Exception {
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
-
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
+ super.setUp();
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -169,7 +142,12 @@ public final class BroadcastQueueModernImplTest {
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
}
/**
@@ -225,11 +203,6 @@ public final class BroadcastQueueModernImplTest {
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
}
- private BroadcastRecord makeOrderedBroadcastRecord(Intent intent) {
- return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
- List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) {
return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false);
}
@@ -246,8 +219,8 @@ public final class BroadcastQueueModernImplTest {
private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
List receivers, IIntentReceiver resultTo, boolean ordered) {
- return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
- null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
+ return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false,
+ null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
}
@@ -259,12 +232,12 @@ public final class BroadcastQueueModernImplTest {
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex, long enqueueTime) {
- queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
- throw new UnsupportedOperationException();
- });
record.enqueueTime = enqueueTime;
record.enqueueRealTime = enqueueTime;
record.enqueueClockTime = enqueueTime;
+ queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
+ throw new UnsupportedOperationException();
+ });
}
@Test
@@ -419,6 +392,7 @@ public final class BroadcastQueueModernImplTest {
assertFalse(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -445,6 +419,7 @@ public final class BroadcastQueueModernImplTest {
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -526,11 +501,13 @@ public final class BroadcastQueueModernImplTest {
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isGreaterThan(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
mConstants.MAX_PENDING_BROADCASTS = 1;
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -549,10 +526,12 @@ public final class BroadcastQueueModernImplTest {
queue.setProcessAndUidState(mProcess, true, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -570,6 +549,7 @@ public final class BroadcastQueueModernImplTest {
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_TOP_PROCESS, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(ActivityManager.PROCESS_STATE_SERVICE).when(mProcess).getSetProcState();
queue.setProcessAndUidState(mProcess, false, false);
@@ -580,6 +560,7 @@ public final class BroadcastQueueModernImplTest {
List.of(makeMockRegisteredReceiver())), 0);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -594,16 +575,19 @@ public final class BroadcastQueueModernImplTest {
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(true).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_PERSISTENT, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(false).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -618,6 +602,7 @@ public final class BroadcastQueueModernImplTest {
assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -636,10 +621,12 @@ public final class BroadcastQueueModernImplTest {
assertEquals(Long.MAX_VALUE, queue.getRunnableAt());
assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
/**
@@ -1575,6 +1562,216 @@ public final class BroadcastQueueModernImplTest {
verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
}
+ @Test
+ public void testDeliveryDeferredForCached() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ @Test
+ public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastOptions optionsPackageChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ optionsPackageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ // TODO: Reuse BroadcastQueueTest.makeActiveProcessRecord()
+ private ProcessRecord makeProcessRecord(ApplicationInfo info) {
+ final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid));
+ r.setPid(mNextPid.incrementAndGet());
+ return r;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final IIntentReceiver receiver = mock(IIntentReceiver.class);
+ final ReceiverList receiverList = new ReceiverList(mAms, app, app.getPid(), app.info.uid,
+ UserHandle.getUserId(app.info.uid), receiver);
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 1c8e9490435d..33645455ca98 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -62,58 +62,36 @@ import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.UidObserver;
import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
-import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerExemptionManager;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.AlarmManagerInternal;
-import com.android.server.DropBoxManagerInternal;
-import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.Injector;
-import com.android.server.appop.AppOpsService;
-import com.android.server.wm.ActivityTaskManagerService;
-
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.Writer;
@@ -126,7 +104,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
@@ -136,13 +113,9 @@ import java.util.function.UnaryOperator;
@MediumTest
@RunWith(Parameterized.class)
@SuppressWarnings("GuardedBy")
-public class BroadcastQueueTest {
+public class BroadcastQueueTest extends BaseBroadcastQueueTest {
private static final String TAG = "BroadcastQueueTest";
- @Rule
- public final ApplicationExitInfoTest.ServiceThreadRule
- mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
-
private final Impl mImpl;
private enum Impl {
@@ -150,30 +123,8 @@ public class BroadcastQueueTest {
MODERN,
}
- private Context mContext;
- private HandlerThread mHandlerThread;
- private TestLooperManager mLooper;
- private AtomicInteger mNextPid;
-
- @Mock
- private AppOpsService mAppOpsService;
- @Mock
- private ProcessList mProcessList;
- @Mock
- private DropBoxManagerInternal mDropBoxManagerInt;
- @Mock
- private PackageManagerInternal mPackageManagerInt;
- @Mock
- private UsageStatsManagerInternal mUsageStatsManagerInt;
- @Mock
- private AlarmManagerInternal mAlarmManagerInt;
-
- private ActivityManagerService mAms;
private BroadcastQueue mQueue;
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
private UidObserver mUidObserver;
- private UidObserver mUidCachedStateObserver;
/**
* Desired behavior of the next
@@ -183,11 +134,6 @@ public class BroadcastQueueTest {
ProcessStartBehavior.SUCCESS);
/**
- * Map from PID to registered registered runtime receivers.
- */
- private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
-
- /**
* Collection of all active processes during current test run.
*/
private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
@@ -208,41 +154,8 @@ public class BroadcastQueueTest {
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ super.setUp();
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
-
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
-
- mNextPid = new AtomicInteger(100);
-
- LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
- LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
- LocalServices.removeServiceForTest(AlarmManagerInternal.class);
- LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
- doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
- doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
- doAnswer((invocation) -> {
- return getUidForPackage(invocation.getArgument(0));
- }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
-
- final ActivityManagerService realAms = new ActivityManagerService(
- new TestInjector(mContext), mServiceThreadRule.getThread());
- realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
- realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
- realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
- realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
- realAms.mPackageManagerInt = mPackageManagerInt;
- realAms.mUsageStatsService = mUsageStatsManagerInt;
- realAms.mProcessesReady = true;
- mAms = spy(realAms);
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
@@ -321,21 +234,11 @@ public class BroadcastQueueTest {
return null;
}).when(mAms).registerUidObserver(any(), anyInt(),
eq(ActivityManager.PROCESS_STATE_TOP), any());
- doAnswer((invocation) -> {
- mUidCachedStateObserver = invocation.getArgument(0);
- return null;
- }).when(mAms).registerUidObserver(any(), anyInt(),
- eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.TIMEOUT = 200;
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -358,7 +261,7 @@ public class BroadcastQueueTest {
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
// Verify that all processes have finished handling broadcasts
for (ProcessRecord app : mActiveProcesses) {
@@ -369,26 +272,9 @@ public class BroadcastQueueTest {
}
}
- private class TestInjector extends Injector {
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
- Handler handler) {
- return mAppOpsService;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
- }
-
- @Override
- public ProcessList getProcessList(ActivityManagerService service) {
- return mProcessList;
- }
+ @Override
+ public String getTag() {
+ return TAG;
}
private enum ProcessStartBehavior {
@@ -534,62 +420,6 @@ public class BroadcastQueueTest {
return Pair.create(app.getPid(), intent.getAction());
}
- static ApplicationInfo makeApplicationInfo(String packageName) {
- return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
- }
-
- static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
- final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = packageName;
- ai.processName = processName;
- ai.uid = getUidForPackage(packageName, userId);
- return ai;
- }
-
- static ResolveInfo withPriority(ResolveInfo info, int priority) {
- info.priority = priority;
- return info;
- }
-
- static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
- filter.setPriority(priority);
- return filter;
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name) {
- return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
- return makeManifestReceiver(packageName, packageName, name, userId);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String processName, String name,
- int userId) {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = packageName;
- ri.activityInfo.processName = processName;
- ri.activityInfo.name = name;
- ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
- return ri;
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
- return makeRegisteredReceiver(app, 0);
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
- final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
- final IntentFilter filter = new IntentFilter();
- filter.setPriority(priority);
- final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
- receiverList.app.info.packageName, null, null, null, receiverList.uid,
- receiverList.userId, false, false, true);
- receiverList.add(res);
- return res;
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
List<Object> receivers) {
return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
@@ -776,41 +606,6 @@ public class BroadcastQueueTest {
eq(userId), anyInt(), anyInt(), any());
}
- static final int USER_GUEST = 11;
-
- static final String PACKAGE_ANDROID = "android";
- static final String PACKAGE_PHONE = "com.android.phone";
- static final String PACKAGE_RED = "com.example.red";
- static final String PACKAGE_GREEN = "com.example.green";
- static final String PACKAGE_BLUE = "com.example.blue";
- static final String PACKAGE_YELLOW = "com.example.yellow";
- static final String PACKAGE_ORANGE = "com.example.orange";
-
- static final String PROCESS_SYSTEM = "system";
-
- static final String CLASS_RED = "com.example.red.Red";
- static final String CLASS_GREEN = "com.example.green.Green";
- static final String CLASS_BLUE = "com.example.blue.Blue";
- static final String CLASS_YELLOW = "com.example.yellow.Yellow";
- static final String CLASS_ORANGE = "com.example.orange.Orange";
-
- static int getUidForPackage(@NonNull String packageName) {
- switch (packageName) {
- case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
- case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
- case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
- case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
- case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
- case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
- case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
- default: throw new IllegalArgumentException();
- }
- }
-
- static int getUidForPackage(@NonNull String packageName, int userId) {
- return UserHandle.getUid(userId, getUidForPackage(packageName));
- }
-
/**
* Baseline verification of common debugging infrastructure, mostly to make
* sure it doesn't crash.
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 37fe8d1d7fff..f45dd391fbc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -2625,7 +2625,7 @@ public class MockingOomAdjusterTests {
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
doReturn(app).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2637,7 +2637,7 @@ public class MockingOomAdjusterTests {
// 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.
app.mServices.updateHasAboveClientLocked();
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(app);
assertTrue(app.mServices.hasAboveClient());
assertNotEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
new file mode 100644
index 000000000000..0006d0ac92aa
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.restore;
+
+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.Mockito.when;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.utils.BackupEligibilityRules;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActiveRestoreSessionTest {
+ private static final String TEST_APP_NAME = "test_app";
+
+ private ActiveRestoreSession mRestoreSession;
+ private ApplicationInfo mTestApp;
+
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private BackupEligibilityRules mBackupEligibilityRules;
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private TransportManager mTransportManager;
+ @Mock private UserManager mUserManager;
+
+ @Rule
+ public final SetFlagsRule mFlagsRule = new SetFlagsRule();
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule
+ .Builder(/* testClassInstance */ this)
+ .mockStatic(LocalServices.class)
+ .afterSessionFinished(
+ () -> LocalServices.removeServiceForTest(PackageManagerInternal.class)
+ ).build();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass */ this);
+ when(mBackupEligibilityRules.isAppEligibleForRestore(any())).thenReturn(true);
+ when(mBackupManagerService.getEligibilityRulesForOperation(anyInt())).thenReturn(
+ mBackupEligibilityRules);
+ when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ when(mBackupManagerService.getContext()).thenReturn(mContext);
+ when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+ when(LocalServices.getService(PackageManagerInternal.class)).thenReturn(
+ mPackageManagerInternal);
+
+ mRestoreSession = new ActiveRestoreSession(mBackupManagerService,
+ /* packageName */ null,
+ /* transportName */ "",
+ mBackupEligibilityRules);
+ mTestApp = new ApplicationInfo();
+ mTestApp.packageName = TEST_APP_NAME;
+ }
+
+ @Test
+ public void testGetBackupEligibilityRules_skipRestoreFlagOn_skipsLaunchedAppRestore() {
+ mFlagsRule.enableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+ RestoreSet restoreSet = new RestoreSet(
+ /* name */ null,
+ /* device */ null,
+ /* token */ 0,
+ /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+ when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+ .thenReturn(true);
+
+ BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+ restoreSet);
+
+ assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isFalse();
+ }
+
+ @Test
+ public void testGetBackupEligibilityRules_skipRestoreFlagOff_allowsAppRestore() {
+ mFlagsRule.disableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+ RestoreSet restoreSet = new RestoreSet(
+ /* name */ null,
+ /* device */ null,
+ /* token */ 0,
+ /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+ when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+ .thenReturn(true);
+
+ BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+ restoreSet);
+
+ assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isTrue();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 030665537c38..290b26027340 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -860,6 +860,66 @@ public class BackupEligibilityRulesTest {
assertThat(result).isFalse();
}
+ @Test
+ public void isAppEligibleForRestore_hasBeenLaunched_returnsFalse() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ true);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_hasNotBeenLaunched_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(false);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_launchedButHasBackupAgent_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ "BackupAgent");
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_doNotSkipRestoreForLaunched_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
private BackupEligibilityRules getBackupEligibilityRules(
@BackupDestination int backupDestination) {
return new BackupEligibilityRules(mPackageManager, mMockPackageManagerInternal, mUserId,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 64e86f9ab1fd..4958f1c1c214 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -24,7 +24,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -38,6 +40,11 @@ import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONS
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.controllers.ConnectivityController.CcConfig.KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY;
+import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_AVOID;
+import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_PREFER;
+import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_UNDEFINED;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +52,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -64,16 +72,19 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
+import android.net.NetworkRequest;
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.DeviceConfig;
import android.telephony.CellSignalStrength;
import android.telephony.SignalStrength;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.DataUnit;
+import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
@@ -114,6 +125,7 @@ public class ConnectivityControllerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Constants mConstants;
+ private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private FlexibilityController mFlexibilityController;
private static final int UID_RED = 10001;
@@ -135,6 +147,9 @@ public class ConnectivityControllerTest {
LocalServices.removeServiceForTest(JobSchedulerInternal.class);
LocalServices.addService(JobSchedulerInternal.class, mock(JobSchedulerInternal.class));
+ mDeviceConfigPropertiesBuilder =
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -164,7 +179,7 @@ public class ConnectivityControllerTest {
when(mPackageManager.hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
mFlexibilityController =
- new FlexibilityController(mService, mock(PrefetchController.class));
+ spy(new FlexibilityController(mService, mock(PrefetchController.class)));
}
@Test
@@ -954,6 +969,12 @@ public class ConnectivityControllerTest {
@Test
public void testUpdates() throws Exception {
+ ConnectivityController.sNetworkTransportAffinities.put(
+ NetworkCapabilities.TRANSPORT_CELLULAR, TRANSPORT_AFFINITY_AVOID);
+ ConnectivityController.sNetworkTransportAffinities.put(
+ NetworkCapabilities.TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER);
+ ConnectivityController.sNetworkTransportAffinities.put(
+ NetworkCapabilities.TRANSPORT_TEST, TRANSPORT_AFFINITY_UNDEFINED);
final ArgumentCaptor<NetworkCallback> callbackCaptor =
ArgumentCaptor.forClass(NetworkCallback.class);
doNothing().when(mConnManager).registerNetworkCallback(any(), callbackCaptor.capture());
@@ -966,9 +987,11 @@ public class ConnectivityControllerTest {
doNothing().when(mConnManager).registerDefaultNetworkCallbackForUid(
eq(UID_BLUE), blueCallbackCaptor.capture(), any());
+ doReturn(true).when(mFlexibilityController).isEnabled();
+
final ConnectivityController controller = new ConnectivityController(mService,
mFlexibilityController);
-
+ InOrder flexControllerInOrder = inOrder(mFlexibilityController);
final Network meteredNet = mock(Network.class);
final NetworkCapabilities meteredCaps = createCapabilitiesBuilder().build();
@@ -976,6 +999,13 @@ public class ConnectivityControllerTest {
final NetworkCapabilities unmeteredCaps = createCapabilitiesBuilder()
.addCapability(NET_CAPABILITY_NOT_METERED)
.build();
+ final NetworkCapabilities meteredWifiCaps = createCapabilitiesBuilder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build();
+ final NetworkCapabilities unmeteredCelullarCaps = createCapabilitiesBuilder()
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
@@ -983,11 +1013,29 @@ public class ConnectivityControllerTest {
final JobStatus blue = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
- assertFalse(red.getPreferUnmetered());
- assertTrue(blue.getPreferUnmetered());
+ final JobStatus red2 = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetwork(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build()),
+ UID_RED);
+ final JobStatus blue2 = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetwork(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build()),
+ UID_BLUE);
+ assertTrue(red.canApplyTransportAffinities());
+ assertTrue(blue.canApplyTransportAffinities());
+ assertFalse(red2.canApplyTransportAffinities());
+ assertFalse(blue2.canApplyTransportAffinities());
controller.maybeStartTrackingJobLocked(red, null);
controller.maybeStartTrackingJobLocked(blue, null);
+ controller.maybeStartTrackingJobLocked(red2, null);
+ controller.maybeStartTrackingJobLocked(blue2, null);
final NetworkCallback generalCallback = callbackCaptor.getValue();
final NetworkCallback redCallback = redCallbackCaptor.getValue();
final NetworkCallback blueCallback = blueCallbackCaptor.getValue();
@@ -997,10 +1045,17 @@ public class ConnectivityControllerTest {
answerNetwork(generalCallback, redCallback, null, null, null);
answerNetwork(generalCallback, blueCallback, null, null, null);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red.getHasAccessToUnmetered());
- assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.getHasAccessToUnmetered());
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
}
// Metered network
@@ -1010,24 +1065,61 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(meteredNet, meteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red.getHasAccessToUnmetered());
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.getHasAccessToUnmetered());
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ // No transport is specified. Accept the network for transport affinity.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertTrue(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // No transport is specified. Avoid the network for transport affinity.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
}
- // Unmetered network background
+ // Unmetered network background for general; metered network for apps
{
answerNetwork(generalCallback, redCallback, meteredNet, meteredNet, meteredCaps);
answerNetwork(generalCallback, blueCallback, meteredNet, meteredNet, meteredCaps);
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red.getHasAccessToUnmetered());
-
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.getHasAccessToUnmetered());
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+
+ // No transport is specified. Accept the network for transport affinity.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertTrue(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // No transport is specified. Avoid the network for transport affinity.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
}
// Lost metered network
@@ -1037,11 +1129,13 @@ public class ConnectivityControllerTest {
generalCallback.onLost(meteredNet);
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red.getHasAccessToUnmetered());
+ // Only the metered network is lost. The unmetered network still satisfies the
+ // affinities.
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.getHasAccessToUnmetered());
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
}
// Specific UID was blocked
@@ -1051,10 +1145,161 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red.getHasAccessToUnmetered());
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.getHasAccessToUnmetered());
+ // No change
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ }
+
+ // Metered wifi
+ {
+ answerNetwork(generalCallback, redCallback, null, meteredNet, meteredWifiCaps);
+ answerNetwork(generalCallback, blueCallback, unmeteredNet, meteredNet, meteredWifiCaps);
+
+ generalCallback.onCapabilitiesChanged(meteredNet, meteredWifiCaps);
+
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+
+ // Wifi is preferred.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertTrue(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertTrue(blue2.areTransportAffinitiesSatisfied());
+ // Wifi is preferred.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertTrue(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertTrue(blue2.areTransportAffinitiesSatisfied());
+ }
+
+ // Unmetered cellular
+ {
+ answerNetwork(generalCallback, redCallback, meteredNet,
+ unmeteredNet, unmeteredCelullarCaps);
+ answerNetwork(generalCallback, blueCallback, meteredNet,
+ unmeteredNet, unmeteredCelullarCaps);
+
+ generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCelullarCaps);
+
+ // Metered network still has wifi transport
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+
+ // Cellular is avoided.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // Cellular is avoided.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ }
+
+ // Remove wifi transport
+ {
+ generalCallback.onCapabilitiesChanged(meteredNet, meteredCaps);
+
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ }
+
+ // Undefined affinity
+ final NetworkCapabilities unmeteredTestCaps = createCapabilitiesBuilder()
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addTransportType(TRANSPORT_TEST)
+ .build();
+ {
+ answerNetwork(generalCallback, redCallback, unmeteredNet,
+ unmeteredNet, unmeteredTestCaps);
+ answerNetwork(generalCallback, blueCallback, unmeteredNet,
+ unmeteredNet, unmeteredTestCaps);
+
+ generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredTestCaps);
+
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+
+ // Undefined is preferred.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+ assertTrue(red.areTransportAffinitiesSatisfied());
+ assertTrue(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // Undefined is avoided.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ }
+
+ // Lost all networks
+ {
+ // Set network as accepted to help confirm onLost notifies flex controller
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+
+ answerNetwork(generalCallback, redCallback, unmeteredNet, null, null);
+ answerNetwork(generalCallback, blueCallback, unmeteredNet, null, null);
+
+ generalCallback.onLost(meteredNet);
+ generalCallback.onLost(unmeteredNet);
+
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // Undefined is avoided.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
}
}
@@ -1113,7 +1358,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = spy(
new ConnectivityController(mService, mFlexibilityController));
doReturn(true).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
doReturn(true).when(controller).isNetworkAvailable(any());
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
@@ -1156,7 +1401,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = spy(
new ConnectivityController(mService, mFlexibilityController));
doReturn(false).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
@@ -1226,7 +1471,7 @@ public class ConnectivityControllerTest {
// Both jobs would still be ready. Exception should not be revoked.
doReturn(true).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
doReturn(true).when(controller).isNetworkAvailable(any());
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, never())
@@ -1234,9 +1479,9 @@ public class ConnectivityControllerTest {
// One job is still ready. Exception should not be revoked.
doReturn(true).when(controller).wouldBeReadyWithConstraintLocked(
- eq(redOne), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ eq(redOne), eq(CONSTRAINT_CONNECTIVITY));
doReturn(false).when(controller).wouldBeReadyWithConstraintLocked(
- eq(redTwo), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ eq(redTwo), eq(CONSTRAINT_CONNECTIVITY));
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, never())
.setAppIdleWhitelist(eq(UID_RED), anyBoolean());
@@ -1244,7 +1489,7 @@ public class ConnectivityControllerTest {
// Both jobs are not ready. Exception should be revoked.
doReturn(false).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, times(1))
.setAppIdleWhitelist(eq(UID_RED), eq(false));
@@ -1311,26 +1556,26 @@ public class ConnectivityControllerTest {
controller.maybeStartTrackingJobLocked(unnetworked, null);
answerNetwork(callback.getValue(), redCallback.getValue(), null, cellularNet, cellularCaps);
- assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
networked.setStandbyBucket(RESTRICTED_INDEX);
unnetworked.setStandbyBucket(RESTRICTED_INDEX);
controller.startTrackingRestrictedJobLocked(networked);
controller.startTrackingRestrictedJobLocked(unnetworked);
- assertFalse(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
// connectivity constraint.
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
networked.setStandbyBucket(RARE_INDEX);
unnetworked.setStandbyBucket(RARE_INDEX);
controller.stopTrackingRestrictedJobLocked(networked);
controller.stopTrackingRestrictedJobLocked(unnetworked);
- assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
// connectivity constraint.
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
}
@Test
@@ -1462,4 +1707,24 @@ public class ConnectivityControllerTest {
return new JobStatus(job.build(), uid, null, -1, 0, null, null,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, 0, null, 0, 0);
}
+
+ private void setDeviceConfigBoolean(ConnectivityController connectivityController,
+ String key, boolean val) {
+ mDeviceConfigPropertiesBuilder.setBoolean(key, val);
+ synchronized (connectivityController.mLock) {
+ connectivityController.prepareForUpdatedConstantsLocked();
+ mFlexibilityController.prepareForUpdatedConstantsLocked();
+ connectivityController.getCcConfig()
+ .processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFlexibilityController.getFcConfig()
+ .processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ connectivityController.onConstantsUpdatedLocked();
+ mFlexibilityController.onConstantsUpdatedLocked();
+ }
+ waitForNonDelayedMessagesProcessed();
+ }
+
+ private void waitForNonDelayedMessagesProcessed() {
+ AppSchedulingModuleThread.getHandler().runWithScissors(() -> {}, 15_000);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index bb9dcf1c85cc..0659f7e9a064 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -19,19 +19,25 @@ package com.android.server.job.controllers;
import static android.app.job.JobInfo.BIAS_FOREGROUND_SERVICE;
import static android.app.job.JobInfo.BIAS_TOP_APP;
import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
+import static android.app.job.JobInfo.NETWORK_TYPE_CELLULAR;
+import static android.app.job.JobInfo.NETWORK_TYPE_NONE;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS;
@@ -54,6 +60,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.net.NetworkRequest;
import android.os.Looper;
import android.provider.DeviceConfig;
import android.util.ArraySet;
@@ -692,16 +699,60 @@ public class FlexibilityControllerTest {
}
@Test
- public void testConnectionToUnMeteredNetwork() {
- JobInfo.Builder jb = createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY);
- JobStatus js = createJobStatus("testTopAppBypass", jb);
+ public void testTransportAffinity() {
+ JobStatus jsAny = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus jsCell = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_CELLULAR));
+ JobStatus jsWifi = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetwork(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build()));
+ // Disable the unseen constraint logic.
+ mFlexibilityController.setConstraintSatisfied(
+ SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, true, FROZEN_TIME);
+ mFlexibilityController.setConstraintSatisfied(
+ SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME);
+ // Require only a single constraint
+ jsAny.adjustNumRequiredFlexibleConstraints(-3);
+ jsCell.adjustNumRequiredFlexibleConstraints(-2);
+ jsWifi.adjustNumRequiredFlexibleConstraints(-2);
synchronized (mFlexibilityController.mLock) {
- js.setHasAccessToUnmetered(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
- js.setHasAccessToUnmetered(true);
- assertEquals(1, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
- js.setHasAccessToUnmetered(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
+ jsAny.setTransportAffinitiesSatisfied(false);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false, FROZEN_TIME);
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // A good network exists, but the network hasn't been assigned to any of the jobs
+ jsAny.setTransportAffinitiesSatisfied(false);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true, FROZEN_TIME);
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // The good network has been assigned to the relevant jobs
+ jsAny.setTransportAffinitiesSatisfied(true);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(true);
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // One job loses access to the network.
+ jsAny.setTransportAffinitiesSatisfied(true);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
}
}
@@ -768,6 +819,131 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testHasEnoughSatisfiedConstraints_unseenConstraints_soonAfterBoot() {
+ // Add connectivity to require 4 constraints
+ JobStatus js = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+
+ // Too soon after boot
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(100 - 1), ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS - 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+
+ // Long after boot
+
+ // No constraints ever seen. Don't bother waiting
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+ }
+
+ @Test
+ public void testHasEnoughSatisfiedConstraints_unseenConstraints_longAfterBoot() {
+ // Add connectivity to require 4 constraints
+ JobStatus connJs = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus nonConnJs = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_NONE));
+
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_BATTERY_NOT_LOW, true,
+ 2 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CHARGING, true,
+ 3 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, true,
+ 4 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ 5 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+
+ // Long after boot
+ // All constraints satisfied right now
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Go down to 2 satisfied
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false,
+ 6 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, false,
+ 7 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ // 3 & 4 constraints were seen recently enough, so the job should wait
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 4 constraints still in the grace period. Wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(16 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 3 constraints still in the grace period. Wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 3 constraints haven't been seen recently. Don't wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(
+ 17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Add then remove connectivity. Resets expectation of 3 constraints for connectivity jobs.
+ // Connectivity job should wait while the non-connectivity job can run.
+ // of getting back to 4 constraints.
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ 18 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false,
+ 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(
+ 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+ }
+
+ @Test
public void testResetJobNumDroppedConstraints() {
JobInfo.Builder jb = createJob(22);
JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
@@ -937,10 +1113,10 @@ public class FlexibilityControllerTest {
ArraySet<JobStatus> jobs = trackedJobs.get(i);
for (int j = 0; j < jobs.size(); j++) {
JobStatus js = jobs.valueAt(j);
- final int isUnMetered = js.getPreferUnmetered()
- && js.getHasAccessToUnmetered() ? 1 : 0;
+ final int transportAffinitySatisfied = js.canApplyTransportAffinities()
+ && js.areTransportAffinitiesSatisfied() ? 1 : 0;
assertEquals(js.getNumRequiredFlexibleConstraints()
- <= numSatisfiedConstraints + isUnMetered,
+ <= numSatisfiedConstraints + transportAffinitySatisfied,
js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 1e65c89643fd..a9f5b14fc48d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -182,6 +182,10 @@ public class PackageArchiverTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
mock(Resources.class));
+ when(mInstallerService.createSessionInternal(any(), any(), any(), anyInt(),
+ anyInt())).thenReturn(1);
+ when(mInstallerService.getExistingDraftSessionId(anyInt(), any(), anyInt())).thenReturn(
+ PackageInstaller.SessionInfo.INVALID_ID);
doReturn(new ParceledListSlice<>(List.of(mock(ResolveInfo.class))))
.when(mPackageManagerService).queryIntentReceivers(any(), any(), any(), anyLong(),
eq(mUserId));
@@ -475,6 +479,7 @@ public class PackageArchiverTest {
/* initialExtras= */ isNull());
Intent intent = intentCaptor.getValue();
assertThat(intent.getFlags() & FLAG_RECEIVER_FOREGROUND).isNotEqualTo(0);
+ assertThat(intent.getIntExtra(PackageInstaller.EXTRA_UNARCHIVE_ID, -1)).isEqualTo(1);
assertThat(intent.getStringExtra(PackageInstaller.EXTRA_UNARCHIVE_PACKAGE_NAME)).isEqualTo(
PACKAGE);
assertThat(
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/OWNERS b/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
index fb62520ff57b..37396f392551 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
@@ -1,3 +1,3 @@
include /services/core/java/com/android/server/power/OWNERS
-per-file ThermalManagerServiceMockingTest.java=wvw@google.com,xwxw@google.com
+per-file ThermalManagerServiceMockingTest.java=file:/THERMAL_OWNERS
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index df46054f0f6f..1838fe884561 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -1081,7 +1081,7 @@ public class PowerStatsServiceTest {
assertThat(result.powerMonitors).isNotNull();
assertThat(Arrays.stream(result.powerMonitors).map(PowerMonitor::getName).toList())
.containsAtLeast(
- "energyconsumer0",
+ "ENERGYCONSUMER0",
"BLUETOOTH/1",
"[channelname0]:channelsubsystem0",
"[channelname1]:channelsubsystem1");
@@ -1131,7 +1131,7 @@ public class PowerStatsServiceTest {
Map<String, PowerMonitor> map =
Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
.collect(Collectors.toMap(PowerMonitor::getName, pm -> pm));
- PowerMonitor consumer1 = map.get("energyconsumer0");
+ PowerMonitor consumer1 = map.get("ENERGYCONSUMER0");
PowerMonitor consumer2 = map.get("BLUETOOTH/1");
PowerMonitor measurement1 = map.get("[channelname0]:channelsubsystem0");
PowerMonitor measurement2 = map.get("[channelname1]:channelsubsystem1");
@@ -1196,6 +1196,6 @@ public class PowerStatsServiceTest {
supportedPowerMonitorsResult = new GetSupportedPowerMonitorsResult();
mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult);
assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
- .map(PowerMonitor::getName).toList()).contains("energyconsumer0");
+ .map(PowerMonitor::getName).toList()).contains("ENERGYCONSUMER0");
}
}
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index ef19ba11fb6d..e89199dc9278 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -39,6 +39,7 @@
crossProfileIntentResolutionStrategy='0'
mediaSharedWithParent='true'
credentialShareableWithParent='false'
+ authAlwaysRequiredToDisableQuietMode='true'
showInSettings='23'
hideInSettingsInQuietMode='true'
inheritDevicePolicy='450'
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 177d72b05549..2d36ff311d4f 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,8 +1,9 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
-per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
+per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
-per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
+per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/OWNERS
per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
+per-file PinnerServiceTest.java = file:/apct-tests/perftests/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index ed0a550015cb..1002fba3d60d 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -22,28 +22,32 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
-import android.testing.AndroidTestingRunner;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.testing.TestableContext;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.server.testutils.FakeDeviceConfigInterface;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,7 +65,8 @@ import java.lang.reflect.Method;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
-@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class PinnerServiceTest {
private static final int KEY_CAMERA = 0;
@@ -76,6 +81,8 @@ public class PinnerServiceTest {
private final ArraySet<String> mUpdatedPackages = new ArraySet<>();
private ResolveInfo mHomePackageResolveInfo;
+ private FakeDeviceConfigInterface mFakeDeviceConfigInterface;
+ private PinnerService.Injector mInjector;
@Before
public void setUp() {
@@ -85,6 +92,8 @@ public class PinnerServiceTest {
Looper.prepare();
}
+ // PinnerService.onStart will add itself as a local service, remove to avoid conflicts.
+ LocalServices.removeServiceForTest(PinnerService.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
@@ -100,6 +109,17 @@ public class PinnerServiceTest {
doReturn(true).when(mockActivityManagerInternal).isUidActive(anyInt());
LocalServices.addService(ActivityManagerInternal.class, mockActivityManagerInternal);
+ // Configure the default state to disable any pinning.
+ TestableResources resources = mContext.getOrCreateTestableResources();
+ resources.addOverride(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles, new String[0]);
+ resources.addOverride(com.android.internal.R.bool.config_pinnerCameraApp, false);
+ resources.addOverride(com.android.internal.R.bool.config_pinnerHomeApp, false);
+ resources.addOverride(com.android.internal.R.bool.config_pinnerAssistantApp, false);
+
+ mFakeDeviceConfigInterface = new FakeDeviceConfigInterface();
+ setDeviceConfigPinnedAnonSize(0);
+
mContext = spy(mContext);
// Get HOME (Launcher) package
@@ -107,6 +127,18 @@ public class PinnerServiceTest {
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
mUpdatedPackages.add(mHomePackageResolveInfo.activityInfo.applicationInfo.packageName);
+
+ mInjector = new PinnerService.Injector() {
+ @Override
+ protected DeviceConfigInterface getDeviceConfigInterface() {
+ return mFakeDeviceConfigInterface;
+ }
+
+ @Override
+ protected void publishBinderService(PinnerService service, Binder binderService) {
+ // Suppress this for testing, it's not needed and causes conflitcs.
+ }
+ };
}
@After
@@ -122,12 +154,12 @@ public class PinnerServiceTest {
}
private void unpinAll(PinnerService pinnerService) throws Exception {
- // unpin all packages
- Method unpinAppMethod = PinnerService.class.getDeclaredMethod("unpinApp", int.class);
- unpinAppMethod.setAccessible(true);
- unpinAppMethod.invoke(pinnerService, KEY_HOME);
- unpinAppMethod.invoke(pinnerService, KEY_CAMERA);
- unpinAppMethod.invoke(pinnerService, KEY_ASSISTANT);
+ Method unpinAppsMethod = PinnerService.class.getDeclaredMethod("unpinApps");
+ unpinAppsMethod.setAccessible(true);
+ unpinAppsMethod.invoke(pinnerService);
+ Method unpinAnonRegionMethod = PinnerService.class.getDeclaredMethod("unpinAnonRegion");
+ unpinAnonRegionMethod.setAccessible(true);
+ unpinAnonRegionMethod.invoke(pinnerService);
}
private void waitForPinnerService(PinnerService pinnerService)
@@ -171,21 +203,37 @@ public class PinnerServiceTest {
}
private int getPinnedSize(PinnerService pinnerService) throws Exception {
- final String totalSizeToken = "Total size: ";
+ return getPinnedSizeImpl(pinnerService, "Total size: ");
+ }
+
+ private int getPinnedAnonSize(PinnerService pinnerService) throws Exception {
+ return getPinnedSizeImpl(pinnerService, "Pinned anon region: ");
+ }
+
+ private int getPinnedSizeImpl(PinnerService pinnerService, String sizeToken) throws Exception {
String dumpOutput = getPinnerServiceDump(pinnerService);
BufferedReader bufReader = new BufferedReader(new StringReader(dumpOutput));
- Optional<Integer> size = bufReader.lines().filter(s -> s.contains(totalSizeToken))
- .map(s -> Integer.valueOf(s.substring(totalSizeToken.length()))).findAny();
+ Optional<Integer> size = bufReader.lines().filter(s -> s.contains(sizeToken))
+ .map(s -> Integer.valueOf(s.substring(sizeToken.length()))).findAny();
return size.orElse(-1);
}
+ private void setDeviceConfigPinnedAnonSize(long size) {
+ mFakeDeviceConfigInterface.setProperty(
+ DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
+ "pin_shared_anon_size",
+ String.valueOf(size),
+ /*makeDefault=*/false);
+ }
+
@Test
+ @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeApp() throws Exception {
// Enable HOME app pinning
- Resources res = mock(Resources.class);
- doReturn(true).when(res).getBoolean(com.android.internal.R.bool.config_pinnerHomeApp);
- when(mContext.getResources()).thenReturn(res);
- PinnerService pinnerService = new PinnerService(mContext);
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.bool.config_pinnerHomeApp, true);
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
ArraySet<Integer> pinKeys = getPinKeys(pinnerService);
assertThat(pinKeys.valueAt(0)).isEqualTo(KEY_HOME);
@@ -201,17 +249,17 @@ public class PinnerServiceTest {
int totalPinnedSizeBytes = getPinnedSize(pinnerService);
assertThat(totalPinnedSizeBytes).isGreaterThan(0);
- // Make sure pinned files are unmapped
unpinAll(pinnerService);
}
@Test
+ @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeAppOnBootCompleted() throws Exception {
// Enable HOME app pinning
- Resources res = mock(Resources.class);
- doReturn(true).when(res).getBoolean(com.android.internal.R.bool.config_pinnerHomeApp);
- when(mContext.getResources()).thenReturn(res);
- PinnerService pinnerService = new PinnerService(mContext);
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.bool.config_pinnerHomeApp, true);
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
ArraySet<Integer> pinKeys = getPinKeys(pinnerService);
assertThat(pinKeys.valueAt(0)).isEqualTo(KEY_HOME);
@@ -227,16 +275,14 @@ public class PinnerServiceTest {
int totalPinnedSizeBytes = getPinnedSize(pinnerService);
assertThat(totalPinnedSizeBytes).isGreaterThan(0);
- // Make sure pinned files are unmapped
unpinAll(pinnerService);
}
@Test
public void testNothingToPin() throws Exception {
// No package enabled for pinning
- Resources res = mock(Resources.class);
- when(mContext.getResources()).thenReturn(res);
- PinnerService pinnerService = new PinnerService(mContext);
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
ArraySet<Integer> pinKeys = getPinKeys(pinnerService);
assertThat(pinKeys).isEmpty();
@@ -252,7 +298,51 @@ public class PinnerServiceTest {
int totalPinnedSizeBytes = getPinnedSize(pinnerService);
assertThat(totalPinnedSizeBytes).isEqualTo(0);
- // Make sure pinned files are unmapped
+ int pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+
+ unpinAll(pinnerService);
+ }
+
+ @Test
+ public void testPinAnonRegion() throws Exception {
+ setDeviceConfigPinnedAnonSize(32768);
+
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
+ waitForPinnerService(pinnerService);
+
+ // Ensure the dump reflects the requested anon region.
+ int pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(32768);
+
+ unpinAll(pinnerService);
+ }
+
+ @Test
+ public void testPinAnonRegionUpdatesOnConfigChange() throws Exception {
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
+ waitForPinnerService(pinnerService);
+
+ // Ensure the PinnerService updates itself when the associated DeviceConfig changes.
+ setDeviceConfigPinnedAnonSize(65536);
+ waitForPinnerService(pinnerService);
+ int pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(65536);
+
+ // Each update should be reflected in the reported status.
+ setDeviceConfigPinnedAnonSize(32768);
+ waitForPinnerService(pinnerService);
+ pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(32768);
+
+ setDeviceConfigPinnedAnonSize(0);
+ waitForPinnerService(pinnerService);
+ // An empty anon region should clear the associated status entry.
+ pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+
unpinAll(pinnerService);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index a2e7cf3f1bb2..fd2cf6d4bb5f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -55,6 +55,10 @@ import android.os.Message;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+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.provider.Settings;
import android.testing.TestableContext;
import android.util.DebugUtils;
@@ -69,6 +73,7 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
@@ -134,6 +139,9 @@ import java.util.function.IntConsumer;
@RunWith(AndroidJUnit4.class)
public class FullScreenMagnificationGestureHandlerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
public static final int STATE_IDLE = 1;
public static final int STATE_ACTIVATED = 2;
public static final int STATE_SHORTCUT_TRIGGERED = 3;
@@ -425,6 +433,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingTripleTap_removesInputLag() {
mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
/* detectTwoFingerTripleTap */ true, /* detectShortcut */ true);
@@ -436,6 +445,18 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testDisablingSingleFingerTripleTapAndTwoFingerTripleTap_removesInputLag() {
+ mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
+ /* detectTwoFingerTripleTap */ false, /* detectShortcut */ true);
+ goFromStateIdleTo(STATE_IDLE);
+ allowEventDelegation();
+ tap();
+ // no fast forward
+ verify(mMgh.getNext(), times(2)).onMotionEvent(any(), any(), anyInt());
+ }
+
+ @Test
public void testLongTapAfterShortcutTriggered_neverLogMagnificationTripleTap() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -510,6 +531,54 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTap_StateIsIdle_shouldInActivated() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+
+ assertIn(STATE_ACTIVATED);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+
+ assertIn(STATE_IDLE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTapAndHold_StateIsIdle_shouldZoomsImmediately() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+
+ assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleSwipeAndHold_StateIsIdle_shouldZoomsImmediately() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerSwipeAndHold();
+
+ assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ }
+
+ @Test
public void testMultiTap_outOfDistanceSlop_shouldInIdle() {
// All delay motion events should be sent, if multi-tap with out of distance slop.
// STATE_IDLE will check if tapCount() < 2.
@@ -1258,6 +1327,30 @@ public class FullScreenMagnificationGestureHandlerTest {
send(upEvent());
}
+ private void twoFingerTap() {
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
+ send(upEvent());
+ }
+
+ private void twoFingerTapAndHold() {
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ fastForward(2000);
+ }
+
+ private void twoFingerSwipeAndHold() {
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer1.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 0));
+ }
+
private void triggerShortcut() {
mMgh.notifyShortcutTriggered();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index 860819911173..cfd0289e5650 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -37,11 +37,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
- * Tests for WindowMagnificationConnectionWrapper. We don't test {@code
- * WindowMagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
+ * Tests for MagnificationConnectionWrapper. We don't test {@code
+ * MagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
* {@link WindowMagnificationManagerTest}.
*/
-public class WindowMagnificationConnectionWrapperTest {
+public class MagnificationConnectionWrapperTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@@ -54,14 +54,14 @@ public class WindowMagnificationConnectionWrapperTest {
private MagnificationAnimationCallback mAnimationCallback;
private MockWindowMagnificationConnection mMockWindowMagnificationConnection;
- private WindowMagnificationConnectionWrapper mConnectionWrapper;
+ private MagnificationConnectionWrapper mConnectionWrapper;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
mConnection = mMockWindowMagnificationConnection.getConnection();
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace);
+ mConnectionWrapper = new MagnificationConnectionWrapper(mConnection, mTrace);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 0f3daec263e0..74eb79d7554c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -28,6 +28,8 @@ import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUT
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -248,6 +250,45 @@ public class AuthSessionTest {
}
@Test
+ public void testOnErrorReceivedBeforeOnDialogAnimatedIn() throws RemoteException {
+ final int fingerprintId = 0;
+ final int faceId = 1;
+ setupFingerprint(fingerprintId, FingerprintSensorProperties.TYPE_REAR);
+ setupFace(faceId, true /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertThat(sensor.getSensorState()).isEqualTo(BiometricSensor.STATE_WAITING_FOR_COOKIE);
+ session.onCookieReceived(
+ session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+ }
+ assertThat(session.allCookiesReceived()).isTrue();
+ assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED);
+
+ final BiometricSensor faceSensor = session.mPreAuthInfo.eligibleSensors.get(faceId);
+ final BiometricSensor fingerprintSensor = session.mPreAuthInfo.eligibleSensors.get(
+ fingerprintId);
+ final int cookie = faceSensor.getCookie();
+ session.onErrorReceived(0, cookie, BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, 0);
+
+ assertThat(faceSensor.getSensorState()).isEqualTo(BiometricSensor.STATE_STOPPED);
+ assertThat(session.getState()).isEqualTo(STATE_ERROR_PENDING_SYSUI);
+
+ session.onDialogAnimatedIn(true);
+
+ assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED_UI_SHOWING);
+ assertThat(fingerprintSensor.getSensorState()).isEqualTo(
+ BiometricSensor.STATE_AUTHENTICATING);
+ }
+
+ @Test
public void testCancelReducesAppetiteForCookies() throws Exception {
setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
mock(IBiometricAuthenticator.class));
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 128f3149e6d4..d662620407e8 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
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.FaceEnrollOptions;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -123,7 +124,7 @@ public class FaceEnrollClientTest {
@Test
public void notifyHalWhenContextChanges() throws RemoteException {
- final FaceEnrollClient client = createClient();
+ final FaceEnrollClient client = createClient(3);
client.start(mCallback);
final ArgumentCaptor<OperationContext> captor =
@@ -143,6 +144,14 @@ public class FaceEnrollClientTest {
verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
}
+ @Test
+ public void enrollWithFaceOptions() throws RemoteException {
+ final FaceEnrollClient client = createClient(4);
+ client.start(mCallback);
+
+ verify(mHal).enrollWithOptions(any());
+ }
+
private FaceEnrollClient createClient() throws RemoteException {
return createClient(200 /* version */);
}
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 7e6883bcec63..ccbbaa52ac21 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
@@ -100,7 +100,7 @@ public class InputControllerTest {
}
@Test
- public void registerInputDevice_deviceCreation_hasDeviceId() {
+ public void registerInputDevice_deviceCreation_hasDeviceId() throws Exception {
final IBinder device1Token = new Binder("device1");
mInputController.createMouse("mouse", /*vendorId= */ 1, /*productId= */ 1, device1Token,
/* displayId= */ 1);
@@ -124,7 +124,7 @@ public class InputControllerTest {
}
@Test
- public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() {
+ public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() throws Exception {
final IBinder deviceToken = new Binder();
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
@@ -137,7 +137,8 @@ public class InputControllerTest {
}
@Test
- public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() {
+ public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride()
+ throws Exception {
final IBinder deviceToken = new Binder();
mInputController.createMouse("mouse1", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
@@ -153,7 +154,7 @@ public class InputControllerTest {
}
@Test
- public void createNavigationTouchpad_hasDeviceId() {
+ public void createNavigationTouchpad_hasDeviceId() throws Exception {
final IBinder deviceToken = new Binder();
mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1,
deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50);
@@ -166,7 +167,7 @@ public class InputControllerTest {
}
@Test
- public void createNavigationTouchpad_setsTypeAssociation() {
+ public void createNavigationTouchpad_setsTypeAssociation() throws Exception {
final IBinder deviceToken = new Binder();
mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1,
deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50);
@@ -176,7 +177,7 @@ public class InputControllerTest {
}
@Test
- public void createAndUnregisterNavigationTouchpad_unsetsTypeAssociation() {
+ public void createAndUnregisterNavigationTouchpad_unsetsTypeAssociation() throws Exception {
final IBinder deviceToken = new Binder();
mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1,
deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50);
@@ -188,7 +189,7 @@ public class InputControllerTest {
}
@Test
- public void createKeyboard_addAndRemoveKeyboardLayoutAssociation() {
+ public void createKeyboard_addAndRemoveKeyboardLayoutAssociation() throws Exception {
final IBinder deviceToken = new Binder("device");
mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, deviceToken,
@@ -201,56 +202,7 @@ public class InputControllerTest {
}
@Test
- public void createInputDevice_tooLongNameRaisesException() {
- final IBinder deviceToken = new Binder("device");
- // The underlying uinput implementation only supports device names up to 80 bytes. This
- // string is all ASCII characters, therefore if we have more than 80 ASCII characters we
- // will have more than 80 bytes.
- String deviceName =
- "This.is.a.very.long.device.name.that.exceeds.the.maximum.length.of.80.bytes"
- + ".by.a.couple.bytes";
-
- assertThrows(RuntimeException.class, () -> {
- mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken,
- 1);
- });
- }
-
- @Test
- public void createInputDevice_tooLongDeviceNameRaisesException() {
- final IBinder deviceToken = new Binder("device");
- // The underlying uinput implementation only supports device names up to 80 bytes (including
- // a 0-byte terminator).
- // This string is 79 characters and 80 bytes (including the 0-byte terminator)
- String deviceName =
- "This.is.a.very.long.device.name.that.exceeds.the.maximum.length01234567890123456";
-
- assertThrows(RuntimeException.class, () -> {
- mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken,
- 1);
- });
- }
-
- @Test
- public void createInputDevice_stringWithLessThanMaxCharsButMoreThanMaxBytesRaisesException() {
- final IBinder deviceToken = new Binder("device1");
-
- // Has only 39 characters but is 109 bytes as utf-8
- String device_name =
- "░▄▄▄▄░\n" +
- "▀▀▄██►\n" +
- "▀▀███►\n" +
- "░▀███►░█►\n" +
- "▒▄████▀▀";
-
- assertThrows(RuntimeException.class, () -> {
- mInputController.createDpad(device_name, /*vendorId= */5, /*productId=*/5,
- deviceToken, 1);
- });
- }
-
- @Test
- public void createInputDevice_duplicateNamesAreNotAllowed() {
+ public void createInputDevice_duplicateNamesAreNotAllowed() throws Exception {
final IBinder deviceToken1 = new Binder("deviceToken1");
final IBinder deviceToken2 = new Binder("deviceToken2");
@@ -258,9 +210,9 @@ public class InputControllerTest {
mInputController.createDpad(sharedDeviceName, /*vendorId= */4, /*productId=*/4,
deviceToken1, 1);
- assertThrows("Device names need to be unique", RuntimeException.class, () -> {
- mInputController.createDpad(sharedDeviceName, /*vendorId= */5, /*productId=*/5,
- deviceToken2, 2);
- });
+ assertThrows("Device names need to be unique",
+ InputController.DeviceCreationException.class,
+ () -> mInputController.createDpad(
+ sharedDeviceName, /*vendorId= */5, /*productId=*/5, deviceToken2, 2));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
deleted file mode 100644
index 8f77e9b8d523..000000000000
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
+++ /dev/null
@@ -1,224 +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.server.companion.virtual;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.Manifest;
-import android.companion.virtual.VirtualDeviceParams;
-import android.companion.virtual.camera.IVirtualCamera;
-import android.companion.virtual.camera.IVirtualCameraSession;
-import android.companion.virtual.camera.VirtualCamera;
-import android.companion.virtual.camera.VirtualCameraConfig;
-import android.companion.virtual.camera.VirtualCameraHalConfig;
-import android.companion.virtual.camera.VirtualCameraSession;
-import android.companion.virtual.camera.VirtualCameraStreamConfig;
-import android.companion.virtual.flags.Flags;
-import android.content.ComponentName;
-import android.graphics.ImageFormat;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.Looper;
-import android.os.RemoteException;
-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.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.AdoptShellPermissionsRule;
-import com.android.server.companion.virtual.camera.IVirtualCameraService;
-import com.android.server.companion.virtual.camera.VirtualCameraController;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashSet;
-import java.util.Set;
-
-@Presubmit
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class VirtualCameraTest {
-
- private static final String PKG = "com.android.virtualcamera";
- private static final String CLS = ".VirtualCameraService";
- public static final String CAMERA_DISPLAY_NAME = "testCamera";
-
- private final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
- private FakeVirtualCameraService mFakeVirtualCameraService;
- private VirtualCameraController mVirtualCameraController;
-
- @Rule public final VirtualDeviceRule mVirtualDeviceRule = new VirtualDeviceRule(mContext);
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
- public AdoptShellPermissionsRule mAdoptShellPermissionsRule =
- new AdoptShellPermissionsRule(
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- Manifest.permission.CREATE_VIRTUAL_DEVICE);
-
- @Before
- public void setUp() {
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> mVirtualCameraController);
- mFakeVirtualCameraService = new FakeVirtualCameraService();
- connectFakeService();
- mVirtualCameraController = new VirtualCameraController(mContext);
- }
-
- private VirtualDeviceImpl createVirtualDevice() {
- return mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
- }
-
- private void connectFakeService() {
- mContext.addMockService(
- ComponentName.createRelative(PKG, CLS), mFakeVirtualCameraService.asBinder());
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void addVirtualCamera() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- virtualDevice.registerVirtualCamera(camera);
-
- assertThat(mFakeVirtualCameraService.mCameras).contains(camera);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void addVirtualCamera_serviceNotReady() {
- TestableContext context =
- new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
- VirtualCameraController virtualCameraController = new VirtualCameraController(context);
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> virtualCameraController);
-
- VirtualDeviceImpl virtualDevice =
- mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- virtualDevice.registerVirtualCamera(camera);
- FakeVirtualCameraService fakeVirtualCameraService = new FakeVirtualCameraService();
-
- // Only add the service after connecting the camera
- virtualCameraController.onServiceConnected(
- ComponentName.createRelative(PKG, CLS), fakeVirtualCameraService.asBinder());
-
- assertThat(fakeVirtualCameraService.mCameras).contains(camera);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void getCameraConfiguration() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
- VirtualCameraConfig config =
- new VirtualCameraConfig.Builder()
- .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
- .setDisplayName(CAMERA_DISPLAY_NAME)
- .setCallback(
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
- () -> virtualCameraSession)
- .build();
-
- VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
-
- VirtualCameraConfig returnedConfig = virtualCamera.getConfig();
- assertThat(returnedConfig).isNotNull();
- assertThat(returnedConfig.getDisplayName()).isEqualTo(CAMERA_DISPLAY_NAME);
- Set<VirtualCameraStreamConfig> streamConfigs = returnedConfig.getStreamConfigs();
- assertThat(streamConfigs).hasSize(1);
- VirtualCameraStreamConfig streamConfig =
- streamConfigs.toArray(new VirtualCameraStreamConfig[0])[0];
- assertThat(streamConfig.format).isEqualTo(ImageFormat.RGB_565);
- assertThat(streamConfig.width).isEqualTo(10);
- assertThat(streamConfig.height).isEqualTo(10);
-
- VirtualCameraHalConfig halConfig = virtualCamera.getHalConfig();
- assertThat(halConfig).isNotNull();
- assertThat(halConfig.displayName).isEqualTo(CAMERA_DISPLAY_NAME);
- assertThat(halConfig.streamConfigs).asList().hasSize(1);
- assertThat(halConfig.streamConfigs[0].format).isEqualTo(ImageFormat.RGB_565);
- assertThat(halConfig.streamConfigs[0].width).isEqualTo(10);
- assertThat(halConfig.streamConfigs[0].height).isEqualTo(10);
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void createCameraWithVirtualCameraInstance() {
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
-
- VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
- VirtualCameraConfig config = createVirtualCameraConfig(virtualCameraSession);
- VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
-
- assertThat(mFakeVirtualCameraService.mCameras).contains(virtualCamera);
- assertThat(virtualCamera.open()).isInstanceOf(IVirtualCameraSession.class);
- }
-
- @RequiresFlagsDisabled(Flags.FLAG_VIRTUAL_CAMERA)
- @Test
- public void createCameraDoesNothingWhenControllerIsNull() {
- mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> null);
- VirtualDeviceImpl virtualDevice = createVirtualDevice();
- IVirtualCamera.Default camera = new IVirtualCamera.Default();
- VirtualCameraConfig config = createVirtualCameraConfig(null);
- virtualDevice.registerVirtualCamera(camera);
-
- assertThat(mFakeVirtualCameraService.mCameras).doesNotContain(camera);
- }
-
- @NonNull
- private static VirtualCameraConfig createVirtualCameraConfig(
- VirtualCameraSession virtualCameraSession) {
- return new VirtualCameraConfig.Builder()
- .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
- .setDisplayName(CAMERA_DISPLAY_NAME)
- .setCallback(
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
- () -> virtualCameraSession)
- .build();
- }
-
- private static class FakeVirtualCameraService extends IVirtualCameraService.Stub {
-
- final Set<IVirtualCamera> mCameras = new HashSet<>();
-
- @Override
- public boolean registerCamera(IVirtualCamera camera) throws RemoteException {
- mCameras.add(camera);
- return true;
- }
-
- @Override
- public void unregisterCamera(IVirtualCamera camera) throws RemoteException {
- mCameras.remove(camera);
- }
- }
-}
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 2598a6bea1c0..d87b8d1cb60e 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
@@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any;
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
@@ -52,9 +53,11 @@ import android.Manifest;
import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
+import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -134,6 +137,8 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -259,10 +264,10 @@ public class VirtualDeviceManagerServiceTest {
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
- private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
- @Mock
private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener;
@Mock
+ private Consumer<String> mPersistentDeviceIdRemovedListener;
+ @Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -364,6 +369,18 @@ public class VirtualDeviceManagerServiceTest {
new Handler(TestableLooper.get(this).getLooper()));
when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mNativeWrapperMock.writeButtonEvent(anyLong(), anyInt(), anyInt(), anyLong()))
+ .thenReturn(true);
+ when(mNativeWrapperMock.writeRelativeEvent(anyLong(), anyFloat(), anyFloat(), anyLong()))
+ .thenReturn(true);
+ when(mNativeWrapperMock.writeScrollEvent(anyLong(), anyFloat(), anyFloat(), anyLong()))
+ .thenReturn(true);
+ when(mNativeWrapperMock.writeKeyEvent(anyLong(), anyInt(), anyInt(), anyLong()))
+ .thenReturn(true);
+ when(mNativeWrapperMock.writeTouchEvent(anyLong(), anyInt(), anyInt(), anyInt(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong()))
+ .thenReturn(true);
+
mInputManagerMockHelper = new InputManagerMockHelper(
TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
// Allow virtual devices to be created on the looper thread for testing.
@@ -374,9 +391,8 @@ public class VirtualDeviceManagerServiceTest {
mCameraAccessController =
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
- mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null,
- null, MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false,
- 0, 0, -1);
+ mAssociationInfo = createAssociationInfo(
+ /* associationId= */ 1, AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
@@ -724,25 +740,36 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
- public void onVirtualDisplayCreatedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
-
- mLocalService.onVirtualDisplayCreated(DISPLAY_ID_1);
+ public void onPersistentDeviceIdsRemoved_listenersNotified() {
+ mLocalService.registerPersistentDeviceIdRemovedListener(mPersistentDeviceIdRemovedListener);
+ mLocalService.onPersistentDeviceIdsRemoved(Set.of(mDeviceImpl.getPersistentDeviceId()));
TestableLooper.get(this).processAllMessages();
- verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID_1);
+ verify(mPersistentDeviceIdRemovedListener).accept(mDeviceImpl.getPersistentDeviceId());
}
@Test
- public void onVirtualDisplayRemovedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
+ public void onCdmAssociationsChanged_persistentDeviceIdRemovedListenersNotified() {
+ mLocalService.registerPersistentDeviceIdRemovedListener(mPersistentDeviceIdRemovedListener);
+ mVdms.onCdmAssociationsChanged(List.of(mAssociationInfo));
+ TestableLooper.get(this).processAllMessages();
- addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ mVdms.onCdmAssociationsChanged(List.of(
+ createAssociationInfo(2, AssociationRequest.DEVICE_PROFILE_APP_STREAMING),
+ createAssociationInfo(3, AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION),
+ createAssociationInfo(4, AssociationRequest.DEVICE_PROFILE_WATCH)));
+ TestableLooper.get(this).processAllMessages();
- mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
+ verify(mPersistentDeviceIdRemovedListener).accept(mDeviceImpl.getPersistentDeviceId());
+
+ mVdms.onCdmAssociationsChanged(Collections.emptyList());
TestableLooper.get(this).processAllMessages();
- verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID_1);
+ verify(mPersistentDeviceIdRemovedListener)
+ .accept(VirtualDeviceImpl.createPersistentDeviceId(2));
+ verify(mPersistentDeviceIdRemovedListener)
+ .accept(VirtualDeviceImpl.createPersistentDeviceId(3));
+ verifyNoMoreInteractions(mPersistentDeviceIdRemovedListener);
}
@Test
@@ -1169,12 +1196,12 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void sendKeyEvent_noFd() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
- .setKeyCode(KeyEvent.KEYCODE_A)
- .setAction(VirtualKeyEvent.ACTION_DOWN).build()));
+ assertThat(mDeviceImpl.sendKeyEvent(BINDER,
+ new VirtualKeyEvent.Builder()
+ .setKeyCode(KeyEvent.KEYCODE_A)
+ .setAction(VirtualKeyEvent.ACTION_DOWN)
+ .build()))
+ .isFalse();
}
@Test
@@ -1187,24 +1214,24 @@ public class VirtualDeviceManagerServiceTest {
InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
- mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
- .setKeyCode(keyCode)
- .setAction(action)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendKeyEvent(BINDER,
+ new VirtualKeyEvent.Builder()
+ .setKeyCode(keyCode)
+ .setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action, eventTimeNanos);
}
@Test
public void sendButtonEvent_noFd() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- mDeviceImpl.sendButtonEvent(BINDER,
- new VirtualMouseButtonEvent.Builder()
- .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK)
- .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
- .build()));
+ assertThat(mDeviceImpl.sendButtonEvent(BINDER,
+ new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK)
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+ .build()))
+ .isFalse();
}
@Test
@@ -1217,38 +1244,24 @@ public class VirtualDeviceManagerServiceTest {
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
- mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
- .setButtonCode(buttonCode)
- .setAction(action)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendButtonEvent(BINDER,
+ new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(buttonCode)
+ .setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action, eventTimeNanos);
}
@Test
- public void sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
- final int fd = 1;
- final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
- final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
- mInputController.addDeviceForTesting(BINDER, fd,
- InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
- INPUT_DEVICE_ID);
- assertThrows(
- IllegalStateException.class,
- () ->
- mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
- .setButtonCode(buttonCode)
- .setAction(action).build()));
- }
-
- @Test
public void sendRelativeEvent_noFd() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- mDeviceImpl.sendRelativeEvent(BINDER,
- new VirtualMouseRelativeEvent.Builder().setRelativeX(
- 0.0f).setRelativeY(0.0f).build()));
+ assertThat(mDeviceImpl.sendRelativeEvent(BINDER,
+ new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(0.0f)
+ .setRelativeY(0.0f)
+ .build()))
+ .isFalse();
}
@Test
@@ -1261,39 +1274,25 @@ public class VirtualDeviceManagerServiceTest {
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
- mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
- .setRelativeX(x)
- .setRelativeY(y)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendRelativeEvent(BINDER,
+ new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(x)
+ .setRelativeY(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos);
}
- @Test
- public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
- final int fd = 1;
- final float x = -0.2f;
- final float y = 0.7f;
- mInputController.addDeviceForTesting(BINDER, fd,
- InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
- INPUT_DEVICE_ID);
- assertThrows(
- IllegalStateException.class,
- () ->
- mDeviceImpl.sendRelativeEvent(BINDER,
- new VirtualMouseRelativeEvent.Builder()
- .setRelativeX(x).setRelativeY(y).build()));
- }
@Test
public void sendScrollEvent_noFd() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- mDeviceImpl.sendScrollEvent(BINDER,
- new VirtualMouseScrollEvent.Builder()
- .setXAxisMovement(-1f)
- .setYAxisMovement(1f).build()));
+ assertThat(mDeviceImpl.sendScrollEvent(BINDER,
+ new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(-1f)
+ .setYAxisMovement(1f)
+ .build()))
+ .isFalse();
}
@Test
@@ -1306,42 +1305,28 @@ public class VirtualDeviceManagerServiceTest {
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
- mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
- .setXAxisMovement(x)
- .setYAxisMovement(y)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendScrollEvent(BINDER,
+ new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(x)
+ .setYAxisMovement(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos);
}
- @Test
- public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
- final int fd = 1;
- final float x = 0.5f;
- final float y = 1f;
- mInputController.addDeviceForTesting(BINDER, fd,
- InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
- INPUT_DEVICE_ID);
- assertThrows(
- IllegalStateException.class,
- () ->
- mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
- .setXAxisMovement(x)
- .setYAxisMovement(y).build()));
- }
@Test
public void sendTouchEvent_noFd() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
- .setX(0.0f)
- .setY(0.0f)
- .setAction(VirtualTouchEvent.ACTION_UP)
- .setPointerId(1)
- .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
- .build()));
+ assertThat(mDeviceImpl.sendTouchEvent(BINDER,
+ new VirtualTouchEvent.Builder()
+ .setX(0.0f)
+ .setY(0.0f)
+ .setAction(VirtualTouchEvent.ACTION_UP)
+ .setPointerId(1)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .build()))
+ .isFalse();
}
@Test
@@ -1356,14 +1341,16 @@ public class VirtualDeviceManagerServiceTest {
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
- mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
- .setX(x)
- .setY(y)
- .setAction(action)
- .setPointerId(pointerId)
- .setToolType(toolType)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendTouchEvent(BINDER,
+ new VirtualTouchEvent.Builder()
+ .setX(x)
+ .setY(y)
+ .setAction(action)
+ .setPointerId(pointerId)
+ .setToolType(toolType)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
Float.NaN, eventTimeNanos);
}
@@ -1382,16 +1369,18 @@ public class VirtualDeviceManagerServiceTest {
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
- mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
- .setX(x)
- .setY(y)
- .setAction(action)
- .setPointerId(pointerId)
- .setToolType(toolType)
- .setPressure(pressure)
- .setMajorAxisSize(majorAxisSize)
- .setEventTimeNanos(eventTimeNanos)
- .build());
+ assertThat(mDeviceImpl.sendTouchEvent(BINDER,
+ new VirtualTouchEvent.Builder()
+ .setX(x)
+ .setY(y)
+ .setAction(action)
+ .setPointerId(pointerId)
+ .setToolType(toolType)
+ .setPressure(pressure)
+ .setMajorAxisSize(majorAxisSize)
+ .setEventTimeNanos(eventTimeNanos)
+ .build()))
+ .isTrue();
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
majorAxisSize, eventTimeNanos);
}
@@ -1884,11 +1873,16 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getPersistentIdForDevice_invalidDeviceId_returnsNull() {
assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_INVALID)).isNull();
- assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_DEFAULT)).isNull();
assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_2)).isNull();
}
@Test
+ public void getPersistentIdForDevice_defaultDeviceId() {
+ assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_DEFAULT)).isEqualTo(
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
public void getPersistentIdForDevice_returnsCorrectId() {
assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_1))
.isEqualTo(mDeviceImpl.getPersistentDeviceId());
@@ -1921,7 +1915,7 @@ public class VirtualDeviceManagerServiceTest {
mRunningAppsChangedCallback,
params,
new DisplayManagerGlobal(mIDisplayManager),
- new VirtualCameraController(mContext));
+ new VirtualCameraController());
mVdms.addVirtualDevice(virtualDeviceImpl);
assertThat(virtualDeviceImpl.getAssociationId()).isEqualTo(mAssociationInfo.getId());
assertThat(virtualDeviceImpl.getPersistentDeviceId())
@@ -1943,6 +1937,14 @@ public class VirtualDeviceManagerServiceTest {
return intent.resolveActivity(packageManager);
}
+ private AssociationInfo createAssociationInfo(int associationId, String deviceProfile) {
+ return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
+ /* tag= */ null, MacAddress.BROADCAST_ADDRESS, /* displayName= */ "", deviceProfile,
+ /* associatedDevice= */ null, /* selfManaged= */ true,
+ /* notifyOnDeviceNearby= */ false, /* revoked= */false, /* timeApprovedMs= */0,
+ /* lastTimeConnectedMs= */0, /* systemDataSyncFlags= */ -1);
+ }
+
/** Helper class to drop permissions temporarily and restore them at the end of a test. */
static final class DropShellPermissionsTemporarily implements AutoCloseable {
DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
deleted file mode 100644
index dbd6c889622a..000000000000
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
+++ /dev/null
@@ -1,222 +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.server.companion.virtual;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.admin.DevicePolicyManager;
-import android.companion.AssociationInfo;
-import android.companion.virtual.IVirtualDeviceActivityListener;
-import android.companion.virtual.IVirtualDeviceSoundEffectListener;
-import android.companion.virtual.VirtualDeviceParams;
-import android.companion.virtual.flags.Flags;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.hardware.display.DisplayManagerGlobal;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.IDisplayManager;
-import android.net.MacAddress;
-import android.os.Binder;
-import android.testing.TestableContext;
-import android.util.ArraySet;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.server.LocalServices;
-import com.android.server.companion.virtual.camera.VirtualCameraController;
-import com.android.server.input.InputManagerInternal;
-import com.android.server.sensors.SensorManagerInternal;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-/** Test rule to generate instances of {@link VirtualDeviceImpl}. */
-public class VirtualDeviceRule implements TestRule {
-
- private static final int DEVICE_OWNER_UID = 50;
- private static final int VIRTUAL_DEVICE_ID = 42;
-
- private final Context mContext;
- private InputController mInputController;
- private CameraAccessController mCameraAccessController;
- private AssociationInfo mAssociationInfo;
- private VirtualDeviceManagerService mVdms;
- private VirtualDeviceManagerInternal mLocalService;
- private VirtualDeviceLog mVirtualDeviceLog;
-
- // Mocks
- @Mock private InputController.NativeWrapper mNativeWrapperMock;
- @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
- @Mock private IDisplayManager mIDisplayManager;
- @Mock private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
- @Mock private DevicePolicyManager mDevicePolicyManagerMock;
- @Mock private InputManagerInternal mInputManagerInternalMock;
- @Mock private SensorManagerInternal mSensorManagerInternalMock;
- @Mock private IVirtualDeviceActivityListener mActivityListener;
- @Mock private IVirtualDeviceSoundEffectListener mSoundEffectListener;
- @Mock private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
- @Mock private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback;
-
- // Test instance suppliers
- private Supplier<VirtualCameraController> mVirtualCameraControllerSupplier;
-
- /**
- * Create a new {@link VirtualDeviceRule}
- *
- * @param context The context to be used with the rule.
- */
- public VirtualDeviceRule(@NonNull Context context) {
- Objects.requireNonNull(context);
- mContext = context;
- }
-
- /**
- * Sets a supplier that will supply an instance of {@link VirtualCameraController}. If the
- * supplier returns null, a new instance will be created.
- */
- public VirtualDeviceRule withVirtualCameraControllerSupplier(
- Supplier<VirtualCameraController> virtualCameraControllerSupplier) {
- mVirtualCameraControllerSupplier = virtualCameraControllerSupplier;
- return this;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- init(new TestableContext(mContext));
- base.evaluate();
- }
- };
- }
-
- private void init(@NonNull TestableContext context) {
- MockitoAnnotations.initMocks(this);
-
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
- doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
- doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
- LocalServices.removeServiceForTest(InputManagerInternal.class);
- LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
-
- LocalServices.removeServiceForTest(SensorManagerInternal.class);
- LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
-
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.uniqueId = "uniqueId";
- doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
- doReturn(Display.INVALID_DISPLAY).when(mDisplayManagerInternalMock)
- .getDisplayIdToMirror(anyInt());
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManagerMock);
-
- // Allow virtual devices to be created on the looper thread for testing.
- final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
- mInputController =
- new InputController(
- mNativeWrapperMock,
- InstrumentationRegistry.getInstrumentation()
- .getContext()
- .getMainThreadHandler(),
- context.getSystemService(WindowManager.class),
- threadVerifier);
- mCameraAccessController =
- new CameraAccessController(context, mLocalService, mCameraAccessBlockedCallback);
-
- mAssociationInfo =
- new AssociationInfo(
- /* associationId= */ 1,
- 0,
- null,
- null,
- MacAddress.BROADCAST_ADDRESS,
- "",
- null,
- null,
- true,
- false,
- false,
- 0,
- 0,
- -1);
-
- mVdms = new VirtualDeviceManagerService(context);
- mLocalService = mVdms.getLocalServiceInstance();
- mVirtualDeviceLog = new VirtualDeviceLog(context);
- }
-
- /**
- * Create a {@link VirtualDeviceImpl} with the required mocks
- *
- * @param params See {@link
- * android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int,
- * VirtualDeviceParams)}
- */
- public VirtualDeviceImpl createVirtualDevice(VirtualDeviceParams params) {
- VirtualCameraController virtualCameraController = mVirtualCameraControllerSupplier.get();
- if (Flags.virtualCamera()) {
- if (virtualCameraController == null) {
- virtualCameraController = new VirtualCameraController(mContext);
- }
- }
-
- VirtualDeviceImpl virtualDeviceImpl =
- new VirtualDeviceImpl(
- mContext,
- mAssociationInfo,
- mVdms,
- mVirtualDeviceLog,
- new Binder(),
- new AttributionSource(
- DEVICE_OWNER_UID,
- "com.android.virtualdevice.test",
- "virtualdevicerule"),
- VIRTUAL_DEVICE_ID,
- mInputController,
- mCameraAccessController,
- mPendingTrampolineCallback,
- mActivityListener,
- mSoundEffectListener,
- mRunningAppsChangedCallback,
- params,
- new DisplayManagerGlobal(mIDisplayManager),
- virtualCameraController);
- mVdms.addVirtualDevice(virtualDeviceImpl);
- return virtualDeviceImpl;
- }
-}
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
new file mode 100644
index 000000000000..01922e08d71d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual.camera;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.companion.virtual.camera.VirtualCameraCallback;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtual.camera.VirtualCameraMetadata;
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtualcamera.IVirtualCameraService;
+import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class VirtualCameraControllerTest {
+
+ private static final int CAMERA_DISPLAY_NAME_RES_ID_1 = 10;
+ private static final int CAMERA_WIDTH_1 = 100;
+ private static final int CAMERA_HEIGHT_1 = 200;
+
+ private static final int CAMERA_DISPLAY_NAME_RES_ID_2 = 11;
+ private static final int CAMERA_WIDTH_2 = 400;
+ private static final int CAMERA_HEIGHT_2 = 600;
+ private static final int CAMERA_FORMAT = ImageFormat.YUV_420_888;
+
+ @Mock
+ private IVirtualCameraService mVirtualCameraServiceMock;
+
+ private VirtualCameraController mVirtualCameraController;
+ private final HandlerExecutor mCallbackHandler =
+ new HandlerExecutor(new Handler(Looper.getMainLooper()));
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock);
+ when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
+ }
+
+ @Test
+ public void registerCamera_registersCamera() throws Exception {
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+
+ ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
+ ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
+ verify(mVirtualCameraServiceMock).registerCamera(any(), configurationCaptor.capture());
+ VirtualCameraConfiguration virtualCameraConfiguration = configurationCaptor.getValue();
+ assertThat(virtualCameraConfiguration.supportedStreamConfigs.length).isEqualTo(1);
+ assertVirtualCameraConfiguration(virtualCameraConfiguration, CAMERA_WIDTH_1,
+ CAMERA_HEIGHT_1, CAMERA_FORMAT);
+ }
+
+ @Test
+ public void unregisterCamera_unregistersCamera() throws Exception {
+ VirtualCameraConfig config = createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+ mVirtualCameraController.unregisterCamera(config);
+
+ verify(mVirtualCameraServiceMock).unregisterCamera(any());
+ }
+
+ @Test
+ public void close_unregistersAllCameras() throws Exception {
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+ mVirtualCameraController.registerCamera(createVirtualCameraConfig(
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+
+ ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
+ ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
+ mVirtualCameraController.close();
+ verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
+ configurationCaptor.capture());
+ List<VirtualCameraConfiguration> virtualCameraConfigurations =
+ configurationCaptor.getAllValues();
+ assertThat(virtualCameraConfigurations).hasSize(2);
+ assertVirtualCameraConfiguration(virtualCameraConfigurations.get(0), CAMERA_WIDTH_1,
+ CAMERA_HEIGHT_1, CAMERA_FORMAT);
+ assertVirtualCameraConfiguration(virtualCameraConfigurations.get(1), CAMERA_WIDTH_2,
+ CAMERA_HEIGHT_2, CAMERA_FORMAT);
+ }
+
+ private VirtualCameraConfig createVirtualCameraConfig(
+ int width, int height, int format, int displayNameResId) {
+ return new VirtualCameraConfig.Builder()
+ .addStreamConfig(width, height, format)
+ .setDisplayNameStringRes(displayNameResId)
+ .setVirtualCameraCallback(mCallbackHandler, createNoOpCallback())
+ .build();
+ }
+
+ private static void assertVirtualCameraConfiguration(
+ VirtualCameraConfiguration configuration, int width, int height, int format) {
+ assertThat(configuration.supportedStreamConfigs[0].width).isEqualTo(width);
+ assertThat(configuration.supportedStreamConfigs[0].height).isEqualTo(height);
+ assertThat(configuration.supportedStreamConfigs[0].pixelFormat).isEqualTo(format);
+ }
+
+ private static VirtualCameraCallback createNoOpCallback() {
+ return new VirtualCameraCallback() {
+
+ @Override
+ public void onStreamConfigured(
+ int streamId,
+ @NonNull Surface surface,
+ @NonNull VirtualCameraStreamConfig streamConfig) {}
+
+ @Override
+ public void onProcessCaptureRequest(
+ int streamId, long frameId, @Nullable VirtualCameraMetadata metadata) {}
+
+ @Override
+ public void onStreamClosed(int streamId) {}
+ };
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index c632727fd420..9e5bea7d135b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -1680,4 +1680,47 @@ public class HdmiCecLocalDeviceTvTest {
assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
}
+
+ @Test
+ public void onAddressAllocated_startRequestActiveSourceAction_playbackActiveSource() {
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, 0x1000);
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.clearResultMessages();
+ mNativeWrapper.onCecMessage(activeSourceFromPlayback);
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+ }
+
+ @Test
+ public void onAddressAllocated_startRequestActiveSourceAction_noActiveSource() {
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 2db46e60aea0..46ead854bded 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -571,6 +571,29 @@ public class JobStoreTest {
}
@Test
+ public void testDebugTagsPersisted() throws Exception {
+ JobInfo ji = new Builder(53, mComponent)
+ .setPersisted(true)
+ .addDebugTag("a")
+ .addDebugTag("b")
+ .addDebugTag("c")
+ .addDebugTag("d")
+ .removeDebugTag("d")
+ .build();
+ final JobStatus js = JobStatus.createFromJobInfo(ji, SOME_UID, null, -1, null, null);
+ mTaskStoreUnderTest.add(js);
+ waitForPendingIo();
+
+ Set<String> expectedTags = Set.of("a", "b", "c");
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Debug tags not correctly persisted",
+ expectedTags, loaded.getJob().getDebugTags());
+ }
+
+ @Test
public void testNamespacePersisted() throws Exception {
final String namespace = "my.test.namespace";
JobInfo.Builder b = new Builder(93, mComponent)
@@ -675,6 +698,22 @@ public class JobStoreTest {
}
@Test
+ public void testTraceTagPersisted() throws Exception {
+ JobInfo ji = new Builder(53, mComponent)
+ .setPersisted(true)
+ .setTraceTag("tag")
+ .build();
+ final JobStatus js = JobStatus.createFromJobInfo(ji, SOME_UID, null, -1, null, null);
+ mTaskStoreUnderTest.add(js);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Trace tag not correctly persisted", "tag", loaded.getJob().getTraceTag());
+ }
+
+ @Test
public void testEstimatedNetworkBytes() throws Exception {
assertPersistedEquals(new JobInfo.Builder(0, mComponent)
.setPersisted(true)
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 1c33d0de4568..18961c0feef9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -34,6 +34,7 @@ import android.service.gatekeeper.IGateKeeperService;
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
+import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;
@@ -203,6 +204,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
+ void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
+ }
+
+ @Override
protected boolean isCredentialSharableWithParent(int userId) {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo.isCloneProfile() || userInfo.isManagedProfile();
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
index 1e73a45a0c22..5aef7a320930 100644
--- a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
@@ -90,7 +90,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
@Test
public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
@@ -105,7 +105,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
callAudioRoutesObserver(audioRoutesInfo);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
}
@@ -117,7 +117,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
}
@@ -135,7 +135,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
}
@@ -155,7 +155,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.selectRoute(null);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
}
@@ -171,7 +171,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
}
@@ -202,7 +202,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.updateVolume(VOLUME_SAMPLE_1);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
}
@@ -222,7 +222,7 @@ public class AudioPoliciesDeviceRouteControllerTest {
mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
- MediaRoute2Info route2Info = mController.getDeviceRoute();
+ MediaRoute2Info route2Info = mController.getSelectedRoute();
assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
}
diff --git a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
index 24e48517f280..aed68a5dc7b5 100644
--- a/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
@@ -104,7 +104,7 @@ public class LegacyDeviceRouteControllerTest {
mOnDeviceRouteChangedListener
);
- MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -129,7 +129,7 @@ public class LegacyDeviceRouteControllerTest {
mOnDeviceRouteChangedListener
);
- MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -243,7 +243,7 @@ public class LegacyDeviceRouteControllerTest {
mOnDeviceRouteChangedListener
);
- MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType);
assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue))
@@ -310,7 +310,7 @@ public class LegacyDeviceRouteControllerTest {
// Simulating wired device being connected.
callAudioRoutesObserver(audioRoutesInfo);
- MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME))
@@ -324,7 +324,7 @@ public class LegacyDeviceRouteControllerTest {
AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
callAudioRoutesObserver(fakeBluetoothAudioRoute);
- MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
@@ -334,12 +334,12 @@ public class LegacyDeviceRouteControllerTest {
@Test
public void updateVolume_differentValue_updatesDeviceRouteVolume() {
- MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue();
- actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ actualMediaRoute = mDeviceRouteController.getSelectedRoute();
assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1);
}
diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
index 949f8e7a6ab0..0e881efd4cdf 100644
--- a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
@@ -221,7 +221,7 @@ public class LockdownVpnTrackerTest {
callCallbacksForNetworkConnect(defaultCallback, mNetwork);
// Vpn is starting
- verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork, TEST_CELL_LP);
+ verify(mVpn).startLegacyVpnPrivileged(mProfile);
verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
argThat(notification -> isExpectedNotification(notification,
R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected)));
@@ -242,7 +242,7 @@ public class LockdownVpnTrackerTest {
// LockdownVpnTracker#handleStateChangedLocked. This is a bug.
// TODO: consider fixing this.
verify(mVpn, never()).stopVpnRunnerPrivileged();
- verify(mVpn, never()).startLegacyVpnPrivileged(any(), any(), any());
+ verify(mVpn, never()).startLegacyVpnPrivileged(any());
verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
}
@@ -302,7 +302,7 @@ public class LockdownVpnTrackerTest {
// Vpn is restarted.
verify(mVpn).stopVpnRunnerPrivileged();
- verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork2, wifiLp);
+ verify(mVpn).startLegacyVpnPrivileged(mProfile);
verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
argThat(notification -> isExpectedNotification(notification,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 2cdfbffda407..13dc12032e7d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -57,7 +57,7 @@ import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
-import com.android.net.flags.Flags;
+import com.android.modules.utils.build.SdkLevel;
import org.junit.After;
import org.junit.Before;
@@ -264,7 +264,7 @@ public class NetworkManagementServiceTest {
verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
mNMService.setDataSaverModeEnabled(true);
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
verify(mCm).setDataSaverEnabled(true);
} else {
verify(mNetdService).bandwidthEnableDataSaver(true);
@@ -284,7 +284,7 @@ public class NetworkManagementServiceTest {
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
mNMService.setDataSaverModeEnabled(false);
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
verify(mCm).setDataSaverEnabled(false);
} else {
verify(mNetdService).bandwidthEnableDataSaver(false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java
index bfd407216c3b..186a27ccdc26 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java
@@ -45,6 +45,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.pm.UserInfo;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
@@ -135,6 +136,53 @@ public class UserJourneyLoggerTest {
}
@Test
+ public void testCreatePrivateProfileUserJourney() {
+ final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
+ final UserJourneyLogger.UserJourneySession session =
+ mUserJourneyLogger.logUserJourneyBegin(-1, USER_JOURNEY_USER_CREATE);
+
+ report1.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ -1,
+ USER_LIFECYCLE_EVENT_CREATE_USER,
+ EVENT_STATE_BEGIN,
+ ERROR_CODE_UNSPECIFIED,
+ 1);
+
+ final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
+ final int profileUserId = 10;
+ UserInfo targetUser =
+ new UserInfo(
+ profileUserId,
+ "test private target user",
+ /* iconPath= */ null,
+ UserInfo.FLAG_PROFILE,
+ UserManager.USER_TYPE_PROFILE_PRIVATE);
+ mUserJourneyLogger.logUserCreateJourneyFinish(0, targetUser);
+
+ report1.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ profileUserId,
+ USER_LIFECYCLE_EVENT_CREATE_USER,
+ EVENT_STATE_FINISH,
+ ERROR_CODE_UNSPECIFIED,
+ 2);
+
+ report2.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ USER_JOURNEY_USER_CREATE,
+ 0,
+ profileUserId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_PRIVATE,
+ UserInfo.FLAG_PROFILE,
+ ERROR_CODE_UNSPECIFIED,
+ 1);
+ }
+
+ @Test
public void testRemoveUserJourney() {
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
@@ -161,6 +209,54 @@ public class UserJourneyLoggerTest {
}
@Test
+ public void testRemovePrivateProfileUserJourneyWithError() {
+ final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
+ final int profileUserId = 10;
+ final UserJourneyLogger.UserJourneySession session =
+ mUserJourneyLogger.logUserJourneyBegin(profileUserId, USER_JOURNEY_USER_REMOVE);
+
+ report1.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ profileUserId,
+ USER_LIFECYCLE_EVENT_REMOVE_USER,
+ EVENT_STATE_BEGIN,
+ ERROR_CODE_UNSPECIFIED,
+ 1);
+
+ final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor();
+ final UserInfo targetUser =
+ new UserInfo(
+ profileUserId,
+ "test private target user",
+ /* iconPath= */ null,
+ UserInfo.FLAG_PROFILE,
+ UserManager.USER_TYPE_PROFILE_PRIVATE);
+ mUserJourneyLogger.logUserJourneyFinishWithError(
+ 0, targetUser, USER_JOURNEY_USER_REMOVE, ERROR_CODE_INCOMPLETE_OR_TIMEOUT);
+
+ report1.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ profileUserId,
+ USER_LIFECYCLE_EVENT_REMOVE_USER,
+ EVENT_STATE_ERROR,
+ ERROR_CODE_INCOMPLETE_OR_TIMEOUT,
+ 2);
+
+ report2.captureAndAssert(
+ mUserJourneyLogger,
+ session.mSessionId,
+ USER_JOURNEY_USER_REMOVE,
+ 0,
+ profileUserId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_PRIVATE,
+ UserInfo.FLAG_PROFILE,
+ ERROR_CODE_INCOMPLETE_OR_TIMEOUT,
+ 1);
+ }
+
+ @Test
public void testStartUserJourney() {
final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor();
final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger
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 c684a7bbf884..57b12251c207 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -67,6 +67,7 @@ public class UserManagerServiceUserPropertiesTest {
.setCrossProfileIntentResolutionStrategy(0)
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
+ .setAuthAlwaysRequiredToDisableQuietMode(false)
.setDeleteAppWithParent(false)
.setAlwaysVisible(false)
.build();
@@ -80,6 +81,7 @@ public class UserManagerServiceUserPropertiesTest {
actualProps.setCrossProfileIntentResolutionStrategy(1);
actualProps.setMediaSharedWithParent(true);
actualProps.setCredentialShareableWithParent(false);
+ actualProps.setAuthAlwaysRequiredToDisableQuietMode(true);
actualProps.setDeleteAppWithParent(true);
actualProps.setAlwaysVisible(true);
@@ -123,6 +125,7 @@ public class UserManagerServiceUserPropertiesTest {
.setInheritDevicePolicy(1732)
.setMediaSharedWithParent(true)
.setDeleteAppWithParent(true)
+ .setAuthAlwaysRequiredToDisableQuietMode(false)
.setAlwaysVisible(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
@@ -131,6 +134,7 @@ public class UserManagerServiceUserPropertiesTest {
orig.setShowInSettings(1437);
orig.setInheritDevicePolicy(9456);
orig.setDeleteAppWithParent(false);
+ orig.setAuthAlwaysRequiredToDisableQuietMode(true);
orig.setAlwaysVisible(false);
// Test every permission level. (Currently, it's linear so it's easy.)
@@ -182,6 +186,8 @@ public class UserManagerServiceUserPropertiesTest {
hasManagePermission);
assertEqualGetterOrThrows(orig::getUseParentsContacts,
copy::getUseParentsContacts, hasManagePermission);
+ assertEqualGetterOrThrows(orig::isAuthAlwaysRequiredToDisableQuietMode,
+ copy::isAuthAlwaysRequiredToDisableQuietMode, hasManagePermission);
// Items requiring hasQueryPermission - put them here using hasQueryPermission.
@@ -242,6 +248,8 @@ public class UserManagerServiceUserPropertiesTest {
.isEqualTo(actual.isMediaSharedWithParent());
assertThat(expected.isCredentialShareableWithParent())
.isEqualTo(actual.isCredentialShareableWithParent());
+ assertThat(expected.isAuthAlwaysRequiredToDisableQuietMode())
+ .isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode());
assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible());
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 20270a817831..48eb5c64f3d1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -89,6 +89,7 @@ public class UserManagerServiceUserTypeTest {
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(false)
+ .setAuthAlwaysRequiredToDisableQuietMode(true)
.setShowInSettings(900)
.setHideInSettingsInQuietMode(true)
.setInheritDevicePolicy(340)
@@ -160,6 +161,8 @@ public class UserManagerServiceUserTypeTest {
.getCrossProfileIntentResolutionStrategy());
assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
+ assertTrue(type.getDefaultUserPropertiesReference()
+ .isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
assertTrue(type.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(340, type.getDefaultUserPropertiesReference()
@@ -306,6 +309,7 @@ public class UserManagerServiceUserTypeTest {
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
+ .setAuthAlwaysRequiredToDisableQuietMode(false)
.setShowInSettings(20)
.setHideInSettingsInQuietMode(false)
.setInheritDevicePolicy(21)
@@ -347,6 +351,8 @@ public class UserManagerServiceUserTypeTest {
assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
assertFalse(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(21, aospType.getDefaultUserPropertiesReference()
@@ -394,6 +400,8 @@ public class UserManagerServiceUserTypeTest {
assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertTrue(aospType.getDefaultUserPropertiesReference()
+ .isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
assertTrue(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(450, aospType.getDefaultUserPropertiesReference()
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 89c6a2201434..2b6d8eda6bb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
import android.annotation.UserIdInt;
@@ -34,6 +35,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.provider.Settings;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -267,6 +269,34 @@ public final class UserManagerTest {
}
@Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE)
+ public void testGetProfilesIncludingCommunal() throws Exception {
+ int mainUserId = mUserManager.getMainUser().getIdentifier();
+ final UserInfo otherUser = createUser("TestUser", /* flags= */ 0);
+ final UserInfo profile = createProfileForUser("Profile",
+ UserManager.USER_TYPE_PROFILE_MANAGED, mainUserId);
+
+ final UserHandle communalProfile = mUserManager.getCommunalProfile();
+
+ final List<UserInfo> mainsActual = mUserManager.getProfilesIncludingCommunal(mainUserId);
+ final List<UserInfo> othersActual = mUserManager.getProfilesIncludingCommunal(otherUser.id);
+
+ final List<Integer> mainsExpected = new ArrayList<>();
+ mainsExpected.add(mainUserId);
+ if (profile != null) mainsExpected.add(profile.id);
+ if (communalProfile != null) mainsExpected.add(communalProfile.getIdentifier());
+ assertEquals(mainsExpected.stream().sorted().toList(),
+ mainsActual.stream().map(ui -> ui.id).sorted().toList());
+
+
+ final List<Integer> othersExpected = new ArrayList<>();
+ othersExpected.add(otherUser.id);
+ if (communalProfile != null) othersExpected.add(communalProfile.getIdentifier());
+ assertEquals(othersExpected.stream().sorted().toList(),
+ othersActual.stream().map(ui -> ui.id).sorted().toList());
+ }
+
+ @Test
public void testPrivateProfile() throws Exception {
UserHandle mainUser = mUserManager.getMainUser();
assumeTrue("Main user is null", mainUser != null);
@@ -301,6 +331,9 @@ public final class UserManagerTest {
.isEqualTo(privateProfileUserProperties.isMediaSharedWithParent());
assertThat(typeProps.isCredentialShareableWithParent())
.isEqualTo(privateProfileUserProperties.isCredentialShareableWithParent());
+ assertThat(typeProps.isAuthAlwaysRequiredToDisableQuietMode())
+ .isEqualTo(privateProfileUserProperties
+ .isAuthAlwaysRequiredToDisableQuietMode());
assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
// Verify private profile parent
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 20c0b0a3af66..ee23a00f27d7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -427,7 +427,7 @@ class AndroidPackageParsingValidationTest {
fun parseMetaDataTag() {
val tag = "meta-data"
validateTagAttr(tag, "name", R.styleable.AndroidManifestMetaData_name, 1024)
- validateTagAttr(tag, "value", R.styleable.AndroidManifestMetaData_value, 4000)
+ validateTagAttr(tag, "value", R.styleable.AndroidManifestMetaData_value, 32_768)
}
@Test
@@ -492,6 +492,40 @@ class AndroidPackageParsingValidationTest {
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
}
+ @Test
+ fun totalMetadataValuesExceedMax_shouldFail() {
+ val value = "x".repeat(8192)
+ var tags = ""
+ repeat(32) { index ->
+ tags += "<meta-data name=\"name$index\" value=\"$value\" />"
+ }
+ var xml = "<application>$tags</application>"
+ try {
+ parseXmlForMetadata(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ try {
+ parseXmlForMetadataRes(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ tags += "<meta-data name=\"last\" value=\"x\" />"
+ xml = "<application>$tags</application>"
+ var e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadata(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadataRes(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ }
+
private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
for (name in passNames) {
@@ -664,6 +698,38 @@ class AndroidPackageParsingValidationTest {
} while (type != XmlPullParser.END_DOCUMENT)
}
+ fun parseXmlForMetadata(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateStrAttr(pullParser, "value", value)
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
+ fun parseXmlForMetadataRes(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateResStrAttr(
+ pullParser,
+ R.styleable.AndroidManifestMetaData_value,
+ value
+ )
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
fun expectedCountErrorMsg(tag: String, parentTag: String) =
"The number of child $tag elements exceeded the max allowed in $parentTag"
diff --git a/services/tests/servicestests/src/com/android/server/power/OWNERS b/services/tests/servicestests/src/com/android/server/power/OWNERS
index ef4c0bf71cd7..fe93ebbf3d8c 100644
--- a/services/tests/servicestests/src/com/android/server/power/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/power/OWNERS
@@ -1,3 +1,3 @@
include /services/core/java/com/android/server/power/OWNERS
-per-file ThermalManagerServiceTest.java=wvw@google.com, xwxw@google.com \ No newline at end of file
+per-file ThermalManagerServiceTest.java=file:/THERMAL_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 13c011ad2f68..44dad593810a 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -18,9 +18,11 @@ package com.android.server.power;
import static com.google.common.truth.Truth.assertThat;
+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.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,6 +30,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +52,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.TemperatureWatcher;
import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
import org.junit.Before;
@@ -65,6 +69,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
/**
* atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
@@ -415,9 +420,9 @@ public class ThermalManagerServiceTest {
@Test
public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
watcher.mSevereThresholds.erase();
- watcher.updateSevereThresholds();
+ watcher.updateThresholds();
assertEquals(1, watcher.mSevereThresholds.size());
assertEquals("skin1", watcher.mSevereThresholds.keyAt(0));
Float threshold = watcher.mSevereThresholds.get("skin1");
@@ -426,9 +431,60 @@ public class ThermalManagerServiceTest {
}
@Test
+ public void testTemperatureWatcherUpdateHeadroomThreshold() {
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ synchronized (watcher.mSamples) {
+ Arrays.fill(watcher.mHeadroomThresholds, Float.NaN);
+ }
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 40, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 49, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 70, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 79, 49);
+ synchronized (watcher.mSamples) {
+ assertArrayEquals(new float[]{Float.NaN, 0.7f, 0.9f, 1.0f, 1.5f, 1.7f, 2.0f},
+ watcher.mHeadroomThresholds, 0.01f);
+ }
+
+ // when another sensor reports different threshold, we expect to see smaller one to be used
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 37, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 52, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 100, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 200, 52);
+ synchronized (watcher.mSamples) {
+ assertArrayEquals(new float[]{Float.NaN, 0.5f, 0.8f, 1.0f, 1.4f, 1.7f, 2.0f},
+ watcher.mHeadroomThresholds, 0.01f);
+ }
+ }
+
+ @Test
+ public void testGetThermalHeadroomThresholdsOnlyReadOnce() throws Exception {
+ float[] expected = new float[]{Float.NaN, 0.1f, 0.2f, 0.3f, 0.4f, Float.NaN, 0.6f};
+ when(mIThermalServiceMock.getThermalHeadroomThresholds()).thenReturn(expected);
+ Map<Integer, Float> thresholds1 = mPowerManager.getThermalHeadroomThresholds();
+ verify(mIThermalServiceMock, times(1)).getThermalHeadroomThresholds();
+ for (int status = PowerManager.THERMAL_STATUS_LIGHT;
+ status <= PowerManager.THERMAL_STATUS_SHUTDOWN; status++) {
+ if (Float.isNaN(expected[status])) {
+ assertFalse(thresholds1.containsKey(status));
+ } else {
+ assertEquals(expected[status], thresholds1.get(status), 0.01f);
+ }
+ }
+ reset(mIThermalServiceMock);
+ Map<Integer, Float> thresholds2 = mPowerManager.getThermalHeadroomThresholds();
+ verify(mIThermalServiceMock, times(0)).getThermalHeadroomThresholds();
+ assertNotSame(thresholds1, thresholds2);
+ assertEquals(thresholds1, thresholds2);
+ }
+
+ @Test
public void testTemperatureWatcherGetSlopeOf() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
- List<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>();
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ List<TemperatureWatcher.Sample> samples = new ArrayList<>();
for (int i = 0; i < 30; ++i) {
samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2)));
}
@@ -437,21 +493,23 @@ public class ThermalManagerServiceTest {
@Test
public void testTemperatureWatcherNormalizeTemperature() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
- assertEquals(0.5f, watcher.normalizeTemperature(25.0f, 40.0f), 0.0f);
+ assertEquals(0.5f,
+ TemperatureWatcher.normalizeTemperature(25.0f, 40.0f), 0.0f);
// Temperatures more than 30 degrees below the SEVERE threshold should be clamped to 0.0f
- assertEquals(0.0f, watcher.normalizeTemperature(0.0f, 40.0f), 0.0f);
+ assertEquals(0.0f,
+ TemperatureWatcher.normalizeTemperature(0.0f, 40.0f), 0.0f);
// Temperatures above the SEVERE threshold should not be clamped
- assertEquals(2.0f, watcher.normalizeTemperature(70.0f, 40.0f), 0.0f);
+ assertEquals(2.0f,
+ TemperatureWatcher.normalizeTemperature(70.0f, 40.0f), 0.0f);
}
@Test
public void testTemperatureWatcherGetForecast() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
- ArrayList<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>();
+ ArrayList<TemperatureWatcher.Sample> samples = new ArrayList<>();
// Add a single sample
samples.add(watcher.createSampleForTesting(0, 25.0f));
@@ -478,7 +536,7 @@ public class ThermalManagerServiceTest {
@Test
public void testTemperatureWatcherGetForecastUpdate() throws Exception {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
// Reduce the inactivity threshold to speed up testing
watcher.mInactivityThresholdMillis = 2000;
@@ -499,7 +557,7 @@ public class ThermalManagerServiceTest {
}
// Helper function to hold mSamples lock, avoid GuardedBy lint errors
- private boolean isWatcherSamplesEmpty(ThermalManagerService.TemperatureWatcher watcher) {
+ private boolean isWatcherSamplesEmpty(TemperatureWatcher watcher) {
synchronized (watcher.mSamples) {
return watcher.mSamples.isEmpty();
}
diff --git a/services/tests/timetests/TEST_MAPPING b/services/tests/timetests/TEST_MAPPING
index b24010ce447a..e549229d000f 100644
--- a/services/tests/timetests/TEST_MAPPING
+++ b/services/tests/timetests/TEST_MAPPING
@@ -1,6 +1,5 @@
{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksTimeServicesTests"
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 4406d831dd93..ea113957dddd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -426,7 +426,7 @@ public class NotificationListenersTest extends UiServiceTestCase {
}
@Test
- public void testOnPackageChanged_removingDisallowedPackage() {
+ public void testOnPackageChanged_removingPackage_removeFromDisallowed() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
@@ -440,6 +440,25 @@ public class NotificationListenersTest extends UiServiceTestCase {
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
.getDisallowedPackages()).isEmpty();
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).isEmpty();
+ }
+
+ @Test
+ public void testOnPackageChanged_notRemovingPackage_staysInDisallowed() {
+ NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ NotificationListenerFilter nlf2 =
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+ mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+ mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+ String[] pkgs = new String[] {"pkg1"};
+ int[] uids = new int[] {243};
+ mListeners.onPackagesChanged(false, pkgs, uids);
+
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).contains(a1);
}
@Test
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 6792cfe6e788..7fb8b30dea06 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,7 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -72,6 +73,7 @@ import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -163,6 +165,7 @@ import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.ICompanionDeviceManager;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.BroadcastReceiver;
@@ -3236,7 +3239,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
- new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
+ new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_onTv", 0,
@@ -3820,6 +3823,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mCompanionMgr.getAssociations(PKG, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -3870,6 +3874,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mCompanionMgr.getAssociations(PKG, mUserId))
.thenReturn(emptyList());
mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
@@ -9922,6 +9927,174 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testRestoreConversationChannel_deleted() throws Exception {
+ // Create parent channel
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ final NotificationChannel originalChannel = new NotificationChannel("id", "name",
+ IMPORTANCE_DEFAULT);
+ NotificationChannel parentChannel = parcelAndUnparcel(originalChannel,
+ NotificationChannel.CREATOR);
+ assertEquals(originalChannel, parentChannel);
+ mBinderService.createNotificationChannels(PKG,
+ new ParceledListSlice(Arrays.asList(parentChannel)));
+
+ //Create deleted conversation channel
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, parentChannel, VALID_CONVO_SHORTCUT_ID);
+ final NotificationChannel conversationChannel =
+ mBinderService.getConversationNotificationChannel(
+ PKG, mUserId, PKG, originalChannel.getId(), false, VALID_CONVO_SHORTCUT_ID);
+ conversationChannel.setDeleted(true);
+
+ //Create notification record
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
+ nb.setChannelId(originalChannel.getId());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, originalChannel);
+ assertThat(nr.getChannel()).isEqualTo(originalChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify that the channel was changed to the conversation channel and restored
+ assertThat(mService.getNotificationRecord(nr.getKey()).isConversation()).isTrue();
+ assertThat(mService.getNotificationRecord(nr.getKey()).getChannel()).isEqualTo(
+ conversationChannel);
+ assertThat(mService.getNotificationRecord(nr.getKey()).getChannel().isDeleted()).isFalse();
+ assertThat(mService.getNotificationRecord(nr.getKey()).getChannel().getDeletedTimeMs())
+ .isEqualTo(-1);
+ }
+
+ @Test
+ public void testDoNotRestoreParentChannel_deleted() throws Exception {
+ // Create parent channel and set as deleted
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ final NotificationChannel originalChannel = new NotificationChannel("id", "name",
+ IMPORTANCE_DEFAULT);
+ NotificationChannel parentChannel = parcelAndUnparcel(originalChannel,
+ NotificationChannel.CREATOR);
+ assertEquals(originalChannel, parentChannel);
+ mBinderService.createNotificationChannels(PKG,
+ new ParceledListSlice(Arrays.asList(parentChannel)));
+ parentChannel.setDeleted(true);
+
+ //Create notification record
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
+ nb.setChannelId(originalChannel.getId());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, originalChannel);
+ assertThat(nr.getChannel()).isEqualTo(originalChannel);
+
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify that the channel was not restored and the notification was not posted
+ assertThat(mService.mChannelToastsSent).contains(mUid);
+ assertThat(mService.getNotificationRecord(nr.getKey())).isNull();
+ assertThat(parentChannel.isDeleted()).isTrue();
+ }
+
+ @Test
+ public void testEnqueueToConversationChannel_notDeleted_doesNotRestore() throws Exception {
+ TestableNotificationManagerService service = spy(mService);
+ PreferencesHelper preferencesHelper = spy(mService.mPreferencesHelper);
+ service.setPreferencesHelper(preferencesHelper);
+ // Create parent channel
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ final NotificationChannel originalChannel = new NotificationChannel("id", "name",
+ IMPORTANCE_DEFAULT);
+ NotificationChannel parentChannel = parcelAndUnparcel(originalChannel,
+ NotificationChannel.CREATOR);
+ assertEquals(originalChannel, parentChannel);
+ mBinderService.createNotificationChannels(PKG,
+ new ParceledListSlice(Arrays.asList(parentChannel)));
+
+ //Create conversation channel
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, parentChannel, VALID_CONVO_SHORTCUT_ID);
+ final NotificationChannel conversationChannel =
+ mBinderService.getConversationNotificationChannel(
+ PKG, mUserId, PKG, originalChannel.getId(), false, VALID_CONVO_SHORTCUT_ID);
+
+ //Create notification record
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
+ nb.setChannelId(originalChannel.getId());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, originalChannel);
+ assertThat(nr.getChannel()).isEqualTo(originalChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify that the channel was changed to the conversation channel and not restored
+ assertThat(service.getNotificationRecord(nr.getKey()).isConversation()).isTrue();
+ assertThat(service.getNotificationRecord(nr.getKey()).getChannel()).isEqualTo(
+ conversationChannel);
+ verify(service, never()).handleSavePolicyFile();
+ verify(preferencesHelper, never()).createNotificationChannel(anyString(),
+ anyInt(), any(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testEnqueueToParentChannel_notDeleted_doesNotRestore() throws Exception {
+ TestableNotificationManagerService service = spy(mService);
+ PreferencesHelper preferencesHelper = spy(mService.mPreferencesHelper);
+ service.setPreferencesHelper(preferencesHelper);
+ // Create parent channel
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ final NotificationChannel originalChannel = new NotificationChannel("id", "name",
+ IMPORTANCE_DEFAULT);
+ NotificationChannel parentChannel = parcelAndUnparcel(originalChannel,
+ NotificationChannel.CREATOR);
+ assertEquals(originalChannel, parentChannel);
+ mBinderService.createNotificationChannels(PKG,
+ new ParceledListSlice(Arrays.asList(parentChannel)));
+
+ //Create deleted conversation channel
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, parentChannel, VALID_CONVO_SHORTCUT_ID);
+ final NotificationChannel conversationChannel =
+ mBinderService.getConversationNotificationChannel(
+ PKG, mUserId, PKG, originalChannel.getId(), false, VALID_CONVO_SHORTCUT_ID);
+
+ //Create notification record without a shortcutId
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId(null);
+ nb.setChannelId(originalChannel.getId());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, originalChannel);
+ assertThat(nr.getChannel()).isEqualTo(originalChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify that the channel is the parent channel and no channel was restored
+ //assertThat(service.getNotificationRecord(nr.getKey()).isConversation()).isFalse();
+ assertThat(service.getNotificationRecord(nr.getKey()).getChannel()).isEqualTo(
+ parentChannel);
+ verify(service, never()).handleSavePolicyFile();
+ verify(preferencesHelper, never()).createNotificationChannel(anyString(),
+ anyInt(), any(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean());
+ }
+
+ @Test
public void testGetConversationsForPackage_hasShortcut() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
@@ -12777,6 +12950,145 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mSnoozeHelper).clearData(anyInt());
}
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_systemCaller_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.isSystemUid = true;
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+ .thenReturn(ImmutableList.of(
+ new AssociationInfo.Builder(1, mUserId, "package")
+ .setDisplayName("My watch")
+ .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .build()));
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_withoutCompat_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void getNotificationPolicy_mappedFromImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ mBinderService.getNotificationPolicy("package");
+
+ verify(zenHelper).getNotificationPolicyFromImplicitZenRule(eq("package"));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_mappedToImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
+ eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_systemCaller_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.isSystemUid = true;
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq("package"), anyString(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+ .thenReturn(ImmutableList.of(
+ new AssociationInfo.Builder(1, mUserId, "package")
+ .setDisplayName("My watch")
+ .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .build()));
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq("package"), anyString(), anyInt(), anyBoolean());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 47f15b8df076..1e3b7282e2f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -19,6 +19,7 @@ import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIM
import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -73,6 +74,14 @@ import java.io.IOException;
public class SnoozeHelperTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String XML_TAG_NAME = "snoozed-notifications";
+ private static final String XML_SNOOZED_NOTIFICATION = "notification";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+ private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+ private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+ private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
@Mock SnoozeHelper.Callback mCallback;
@Mock AlarmManager mAm;
@Mock ManagedServices.UserProfiles mUserProfiles;
@@ -316,6 +325,53 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testSnoozeLimit_maximumPersisted() throws XmlPullParserException, IOException {
+ final long snoozeTimeout = 1234;
+ final String snoozeContext = "ctx";
+ // Serialize & deserialize notifications so that only persisted lists are used
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_NAME);
+ // Serialize maximum number of timed + context snoozed notifications, half of each
+ for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++) {
+ final boolean timedNotification = i % 2 == 0;
+ if (timedNotification) {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ serializer.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, 1);
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, "key" + i);
+ if (timedNotification) {
+ serializer.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME, snoozeTimeout);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID, snoozeContext);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ }
+ serializer.endTag(null, XML_TAG_NAME);
+ serializer.endDocument();
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser, 1);
+ // Verify that we can't snooze any more notifications
+ // and that the limit is caused by persisted notifications
+ assertThat(mSnoozeHelper.canSnooze(1)).isFalse();
+ assertThat(mSnoozeHelper.isSnoozed(UserHandle.USER_SYSTEM, "pkg", "key0")).isFalse();
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", "key0")).isEqualTo(snoozeTimeout);
+ assertThat(
+ mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ "key1")).isEqualTo(snoozeContext);
+ }
+
+ @Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -611,6 +667,7 @@ public class SnoozeHelperTest extends UiServiceTestCase {
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
+ final int snoozeDuration = 1000;
IntArray profileIds = new IntArray();
profileIds.add(UserHandle.USER_SYSTEM);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
@@ -618,10 +675,44 @@ public class SnoozeHelperTest extends UiServiceTestCase {
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(
"pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
- mSnoozeHelper.snooze(r, 1000);
- mSnoozeHelper.snooze(r2, 1000);
+ final long snoozeTime = System.currentTimeMillis() + snoozeDuration;
+ mSnoozeHelper.snooze(r, snoozeDuration);
+ mSnoozeHelper.snooze(r2, snoozeDuration);
+ assertEquals(2, mSnoozeHelper.getSnoozed().size());
+ assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isAtLeast(snoozeTime);
+
+ mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
+
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
+ verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r2, false);
+
+ assertEquals(1, mSnoozeHelper.getSnoozed().size());
+ assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isEqualTo(0);
+ }
+
+ @Test
+ public void snoozeWithContext_repostGroupSummary_removesPersisted() throws Exception {
+ final String snoozeContext = "zzzzz";
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ NotificationRecord r = getNotificationRecord(
+ "pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
+ NotificationRecord r2 = getNotificationRecord(
+ "pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
+ mSnoozeHelper.snooze(r, snoozeContext);
+ mSnoozeHelper.snooze(r2, snoozeContext);
assertEquals(2, mSnoozeHelper.getSnoozed().size());
assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isEqualTo(snoozeContext);
mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
@@ -630,6 +721,9 @@ public class SnoozeHelperTest extends UiServiceTestCase {
assertEquals(1, mSnoozeHelper.getSnoozed().size());
assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isNull();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 27e8f3664a65..8f30f413d4d0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -54,6 +54,15 @@ public class TestableNotificationManagerService extends NotificationManagerServi
return mRankingHelper;
}
+ /**
+ * Sets {@link #isSystemUid} and {@link #isSystemAppId} to {@code false}, so that calls to NMS
+ * methods don't succeed {@link #isCallingUidSystem()} and similar checks.
+ */
+ void setCallerIsNormalPackage() {
+ isSystemUid = false;
+ isSystemAppId = false;
+ }
+
@Override
protected boolean isCallingUidSystem() {
countSystemChecks++;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
new file mode 100644
index 000000000000..6cc1c4365fca
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static com.android.server.notification.ZenAdapters.notificationPolicyToZenPolicy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenPolicy;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenAdaptersTest extends UiServiceTestCase {
+
+ @Test
+ public void notificationPolicyToZenPolicy_allCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_ANY, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_starredCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_STARRED,
+ 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_STARRED);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_repeatCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getPriorityCategoryRepeatCallers()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_noCallers() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_conversationsAllowedSendersUnset() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CONVERSATIONS, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(ZenPolicy.STATE_UNSET);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_conversationsNotAllowedSendersUnset() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(
+ ZenPolicy.STATE_DISALLOW);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_setEffects() {
+ Policy policy = new Policy(0, 0, 0,
+ Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+
+ assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_unsetEffects() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_UNSET);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
new file mode 100644
index 000000000000..999e33c24322
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.service.notification.ZenDeviceEffects;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ZenDeviceEffectsTest extends UiServiceTestCase {
+
+ @Test
+ public void builder() {
+ ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTapToWake(true).setShouldDisableTapToWake(false)
+ .setShouldDisableTiltToWake(true)
+ .setShouldMaximizeDoze(true)
+ .setShouldUseNightMode(false)
+ .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
+ assertThat(deviceEffects.shouldDisableAutoBrightness()).isFalse();
+ assertThat(deviceEffects.shouldDisableTapToWake()).isFalse();
+ assertThat(deviceEffects.shouldDisableTiltToWake()).isTrue();
+ assertThat(deviceEffects.shouldDisableTouch()).isFalse();
+ assertThat(deviceEffects.shouldDisplayGrayscale()).isFalse();
+ assertThat(deviceEffects.shouldMaximizeDoze()).isTrue();
+ assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
+ assertThat(deviceEffects.shouldUseNightMode()).isFalse();
+ assertThat(deviceEffects.shouldSuppressAmbientDisplay()).isTrue();
+ }
+
+ @Test
+ public void builder_fromInstance() {
+ ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldUseNightMode(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ ZenDeviceEffects modified = new ZenDeviceEffects.Builder(original)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(false)
+ .build();
+
+ assertThat(modified.shouldDimWallpaper()).isTrue(); // from original
+ assertThat(modified.shouldDisableTiltToWake()).isTrue(); // from original
+ assertThat(modified.shouldDisplayGrayscale()).isTrue(); // updated
+ assertThat(modified.shouldUseNightMode()).isFalse(); // updated
+ assertThat(modified.shouldSuppressAmbientDisplay()).isTrue(); // from original
+ }
+
+ @Test
+ public void writeToParcel_parcelsAndUnparcels() {
+ ZenDeviceEffects source = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldUseNightMode(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ ZenDeviceEffects copy;
+ try {
+ source.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ copy = ZenDeviceEffects.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+
+ assertThat(copy.shouldDimWallpaper()).isTrue();
+ assertThat(copy.shouldDisableTouch()).isTrue();
+ assertThat(copy.shouldMinimizeRadioUsage()).isTrue();
+ assertThat(copy.shouldUseNightMode()).isTrue();
+ assertThat(copy.shouldSuppressAmbientDisplay()).isTrue();
+ assertThat(copy.shouldDisplayGrayscale()).isFalse();
+ }
+
+ @Test
+ public void hasEffects_none_returnsFalse() {
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder().build();
+ assertThat(effects.hasEffects()).isFalse();
+ }
+
+ @Test
+ public void hasEffects_some_returnsTrue() {
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build();
+ assertThat(effects.hasEffects()).isTrue();
+ }
+}
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 261b5d33b635..d4661075a552 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -33,6 +33,7 @@ import android.os.Parcel;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenPolicy;
@@ -327,7 +328,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.conditionId = CONDITION_ID;
rule.condition = CONDITION;
rule.enabled = ENABLED;
- rule.creationTime = 123;
rule.id = "id";
rule.zenMode = INTERRUPTION_FILTER;
rule.modified = true;
@@ -335,6 +335,18 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.snoozing = true;
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
+ rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(false)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(false)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(false)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(false)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(false)
+ .setShouldMaximizeDoze(true)
+ .build();
rule.creationTime = CREATION_TIME;
rule.allowManualInvocation = ALLOW_MANUAL;
@@ -362,6 +374,8 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.name, fromXml.name);
assertEquals(rule.zenMode, fromXml.zenMode);
assertEquals(rule.creationTime, fromXml.creationTime);
+ assertEquals(rule.zenPolicy, fromXml.zenPolicy);
+ assertEquals(rule.zenDeviceEffects, fromXml.zenDeviceEffects);
assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation);
assertEquals(rule.type, fromXml.type);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index fd3d5e9bf863..ed7e8ae5c6e5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -28,8 +30,10 @@ import android.content.ComponentName;
import android.net.Uri;
import android.provider.Settings;
import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeDiff;
+import android.service.notification.ZenModeDiff.RuleDiff;
import android.service.notification.ZenPolicy;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,10 +46,14 @@ import com.android.server.UiServiceTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
@SmallTest
@@ -56,6 +64,15 @@ public class ZenModeDiffTest extends UiServiceTestCase {
public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS =
Set.of("version", "manualRule", "automaticRules");
+ // Differences for flagged fields are only generated if the flag is enabled.
+ // TODO: b/310620812 - Remove this exempt list when flag is inlined.
+ private static final Set<String> ZEN_RULE_EXEMPT_FIELDS =
+ android.app.Flags.modesApi()
+ ? Set.of()
+ : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
+ RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
+ RuleDiff.FIELD_ZEN_DEVICE_EFFECTS);
+
@Test
public void testRuleDiff_addRemoveSame() {
// Test add, remove, and both sides same
@@ -86,7 +103,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
ArrayMap<String, Object> expectedTo = new ArrayMap<>();
List<Field> fieldsForDiff = getFieldsForDiffCheck(
- ZenModeConfig.ZenRule.class, Set.of()); // actually no exempt fields for ZenRule
+ ZenModeConfig.ZenRule.class, ZEN_RULE_EXEMPT_FIELDS);
generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo);
ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
@@ -230,16 +247,21 @@ public class ZenModeDiffTest extends UiServiceTestCase {
rule.name = "name";
rule.snoozing = true;
rule.pkg = "a";
- rule.allowManualInvocation = true;
- rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
- rule.iconResId = 123;
- rule.triggerDescription = "At night";
+ if (android.app.Flags.modesApi()) {
+ rule.allowManualInvocation = true;
+ rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
+ rule.iconResId = 123;
+ rule.triggerDescription = "At night";
+ rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build();
+ }
return rule;
}
// Get the fields on which we would want to check a diff. The requirements are: not final or/
// static (as these should/can never change), and not in a specific list that's exempted.
- private List<Field> getFieldsForDiffCheck(Class c, Set<String> exemptNames)
+ private List<Field> getFieldsForDiffCheck(Class<?> c, Set<String> exemptNames)
throws SecurityException {
Field[] fields = c.getDeclaredFields();
ArrayList<Field> out = new ArrayList<>();
@@ -272,7 +294,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
f.setAccessible(true);
// Just double-check also that the fields actually are for the class declared
assertEquals(f.getDeclaringClass(), a.getClass());
- Class t = f.getType();
+ Class<?> t = f.getType();
// handle the full set of primitive types first
if (boolean.class.equals(t)) {
f.setBoolean(a, true);
@@ -305,8 +327,8 @@ public class ZenModeDiffTest extends UiServiceTestCase {
f.set(a, null);
expectedA.put(f.getName(), null);
try {
- f.set(b, t.getDeclaredConstructor().newInstance());
- expectedB.put(f.getName(), t.getDeclaredConstructor().newInstance());
+ f.set(b, newInstanceOf(t));
+ expectedB.put(f.getName(), newInstanceOf(t));
} catch (Exception e) {
// No default constructor, or blithely attempting to construct something doesn't
// work for some reason. If the default value isn't null, then keep it.
@@ -321,4 +343,34 @@ public class ZenModeDiffTest extends UiServiceTestCase {
}
}
}
+
+ private static Object newInstanceOf(Class<?> clazz) throws ReflectiveOperationException {
+ try {
+ Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();
+ return defaultConstructor.newInstance();
+ } catch (Exception e) {
+ // No default constructor, continue below.
+ }
+
+ // Look for a suitable builder.
+ Optional<Class<?>> clazzBuilder =
+ Arrays.stream(clazz.getDeclaredClasses())
+ .filter(maybeBuilder -> maybeBuilder.getSimpleName().equals("Builder"))
+ .filter(maybeBuilder ->
+ Arrays.stream(maybeBuilder.getMethods()).anyMatch(
+ m -> m.getName().equals("build")
+ && m.getParameterCount() == 0
+ && m.getReturnType().equals(clazz)))
+ .findFirst();
+ if (clazzBuilder.isPresent()) {
+ Object builder = newInstanceOf(clazzBuilder.get());
+ Method buildMethod = builder.getClass().getMethod("build");
+ Object built = buildMethod.invoke(builder);
+ assertThat(built).isInstanceOf(clazz);
+ return built;
+ }
+
+ throw new ReflectiveOperationException(
+ "Sorry! Couldn't figure out how to create an instance of " + clazz.getName());
+ }
}
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 0349ad9e30c7..37aeb57f728e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,7 +17,12 @@
package com.android.server.notification;
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
@@ -28,6 +33,7 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -36,8 +42,11 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
@@ -46,6 +55,8 @@ import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -68,6 +79,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -78,6 +90,7 @@ import android.app.NotificationManager.Policy;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -88,6 +101,7 @@ import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.VolumePolicy;
import android.net.Uri;
+import android.os.Parcel;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -96,6 +110,7 @@ import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
import android.test.suitebuilder.annotation.SmallTest;
@@ -120,6 +135,7 @@ import com.android.server.UiServiceTestCase;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Correspondence;
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
@@ -141,6 +157,8 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@@ -151,27 +169,29 @@ public class ZenModeHelperTest extends UiServiceTestCase {
private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
private static final String CUSTOM_PKG_NAME = "not.android";
+ private static final String CUSTOM_APP_LABEL = "This is not Android";
private static final int CUSTOM_PKG_UID = 1;
private static final String CUSTOM_RULE_ID = "custom_rule";
- private final String NAME = "name";
- private final ComponentName OWNER = new ComponentName("pkg", "cls");
- private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
- private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
- private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+ private static final String NAME = "name";
+ private static final ComponentName OWNER = new ComponentName("pkg", "cls");
+ private static final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+ private static final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+ private static final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
.authority("authority")
.appendPath("path")
.appendPath("test")
.build();
- private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
- private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
- private final int TYPE = TYPE_BEDTIME;
- private final boolean ALLOW_MANUAL = true;
- private final int ICON_RES_ID = 1234;
- private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
- private final boolean ENABLED = true;
- private final int CREATION_TIME = 123;
+ private static final Condition CONDITION = new Condition(CONDITION_ID, "",
+ Condition.STATE_TRUE);
+ private static final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+ private static final int TYPE = TYPE_BEDTIME;
+ private static final boolean ALLOW_MANUAL = true;
+ private static final int ICON_RES_ID = 1234;
+ private static final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+ private static final boolean ENABLED = true;
+ private static final int CREATION_TIME = 123;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -221,6 +241,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.thenReturn(CUSTOM_PKG_UID);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[] {pkg});
+ ApplicationInfo mockAppInfo = mock(ApplicationInfo.class);
+ when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
+ when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
+ .thenReturn(mockAppInfo);
mZenModeHelper.mPm = mPackageManager;
mZenModeEventLogger.reset();
@@ -328,7 +352,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOff_NoMuteApplied() {
- mZenModeHelper.mZenMode = Settings.Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
mZenModeHelper.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS
| PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
@@ -629,7 +653,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 3. apply zen off - verify zen is set to previous ringer (normal)
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -715,7 +739,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 3. apply zen off - verify ringer remains normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -740,7 +764,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 3. apply zen-off - verify ringer is still silent
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
mZenModeHelper.TAG);
@@ -775,7 +799,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 4. apply zen off - verify ringer still silenced
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
mZenModeHelper.TAG);
@@ -789,7 +813,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
@@ -803,7 +827,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void testSilentRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
@@ -830,7 +854,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void testVibrateRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
@@ -1203,7 +1227,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.allowMedia(false)
.allowRepeatCallers(false)
.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
- .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+ .allowMessages(PEOPLE_TYPE_CONTACTS)
.allowEvents(true)
.allowReminders(false)
.build();
@@ -2017,10 +2041,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
// and also that it works to turn it back off again
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
- assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+ assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
@Test
@@ -2035,7 +2059,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
false);
// In total, this should be 2 loggable changes
@@ -2054,7 +2078,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// - resulting DNDPolicyProto the same as the values in setupZenConfig()
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2074,7 +2098,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
@@ -2138,7 +2162,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// - zen policy is the same as the set-up zen config
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2151,7 +2175,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
@@ -2195,7 +2219,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
// Change the policy again
@@ -2299,7 +2323,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// what the event should reflect. At this time, the policy is the same as initial setup.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
@@ -2349,7 +2373,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
- assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+ assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
// but still, nothing should've been logged
assertEquals(0, mZenModeEventLogger.numLoggedChanges());
@@ -2477,7 +2501,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
true);
// Turn off manual mode, call from a package: don't reset UID even though enabler is set
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null,
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null,
CUSTOM_PKG_NAME, "", 12345, false);
// And likewise when turning it back on again
@@ -2654,8 +2678,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- public void testCreateAutomaticZenRule_allFields() {
+ public void zenRuleToAutomaticZenRule_allFields() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
+ new String[] {OWNER.getPackageName()});
+
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = CONFIG_ACTIVITY;
rule.component = OWNER;
@@ -2676,7 +2703,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
rule.iconResId = ICON_RES_ID;
rule.triggerDescription = TRIGGER_DESC;
- AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule);
+ mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
+ AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(rule.id);
assertEquals(NAME, actual.getName());
assertEquals(OWNER, actual.getOwner());
@@ -2694,8 +2722,468 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(TRIGGER_DESC, actual.getTriggerDescription());
}
+ @Test
+ public void testUpdateAutomaticRule_disabled_triggersBroadcast() throws Exception {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ zenRule.setEnabled(false);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_enabled_triggersBroadcast() throws Exception {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ zenRule.setEnabled(true);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[2];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ int i = 0;
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[i++] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[2];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ int i = 0;
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[i++] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_FALSE),
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_unsnoozes() throws IllegalArgumentException {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // Event 2: Snooze rule by turning off DND
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ // Event 3: "User" turns off the automatic rule (sets it to not enabled)
+ zenRule.setEnabled(false);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ null, true));
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, null, true));
+ }
+
+ @Test
+ 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,
+ 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,
+ ZEN_MODE_OFF);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+ .isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_OFF);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+ .isEqualTo(STATE_TRUE);
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
+ mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ withoutWtfCrash(
+ () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+
+ ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowCalls(PEOPLE_TYPE_CONTACTS)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .hideAllVisualEffects()
+ .build();
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ expectedZenPolicy, /* conditionActive= */ null));
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ original);
+
+ // Change priorityCallSenders: contacts -> starred.
+ Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+
+ ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowCalls(PEOPLE_TYPE_STARRED)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .hideAllVisualEffects()
+ .build();
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ expectedZenPolicy, /* conditionActive= */ null));
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
+ mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ withoutWtfCrash(
+ () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
+ CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ writtenPolicy);
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isEqualTo(writtenPolicy);
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_returnsGlobalPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+ mZenModeHelper.mConfig.allowCalls = true;
+ mZenModeHelper.mConfig.allowConversations = false;
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isNotNull();
+ assertThat(readPolicy.allowCalls()).isTrue();
+ assertThat(readPolicy.allowConversations()).isFalse();
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ mZenModeHelper.mConfig.allowCalls = true;
+ mZenModeHelper.mConfig.allowConversations = false;
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isNotNull();
+ assertThat(readPolicy.allowCalls()).isTrue();
+ assertThat(readPolicy.allowConversations()).isFalse();
+ }
+
+ private static final Correspondence<ZenRule, ZenRule> IGNORE_TIMESTAMPS =
+ Correspondence.transforming(zr -> {
+ Parcel p = Parcel.obtain();
+ try {
+ zr.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ZenRule copy = new ZenRule(p);
+ copy.creationTime = 0;
+ return copy;
+ } finally {
+ p.recycle();
+ }
+ },
+ "Ignoring timestamps");
+
+ private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
+ @Nullable Boolean conditionActive) {
+ ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.id = "implicit_" + ownerPkg;
+ rule.conditionId = Uri.parse("condition://android/implicit/" + ownerPkg);
+ if (conditionActive != null) {
+ rule.condition = conditionActive
+ ? new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_activated), STATE_TRUE)
+ : new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_deactivated),
+ STATE_FALSE);
+ }
+ rule.zenMode = zenMode;
+ rule.zenPolicy = policy;
+ rule.pkg = ownerPkg;
+ rule.name = CUSTOM_APP_LABEL;
+ rule.enabled = true;
+ return rule;
+ }
+
private void setupZenConfig() {
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;
mZenModeHelper.mConfig.allowMedia = false;
mZenModeHelper.mConfig.allowSystem = false;
@@ -2744,6 +3232,15 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(STATE_ALLOW, dndProto.getNotificationList().getNumber());
}
+ private static void withoutWtfCrash(Runnable test) {
+ Log.TerribleFailureHandler oldHandler = Log.setWtfHandler((tag, what, system) -> {});
+ try {
+ test.run();
+ } finally {
+ Log.setWtfHandler(oldHandler);
+ }
+ }
+
/**
* Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
*/
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 7a2bb5a90846..f0803418376f 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -75,8 +75,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.util.ArraySet;
-import android.view.Display;
import androidx.test.InstrumentationRegistry;
@@ -103,7 +101,7 @@ public class VibrationSettingsTest {
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int UID = 1;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -137,9 +135,6 @@ public class VibrationSettingsTest {
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private BroadcastReceiver mRegisteredBatteryBroadcastReceiver;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
@Before
public void setUp() throws Exception {
@@ -155,14 +150,6 @@ public class VibrationSettingsTest {
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
removeServicesForTest();
addServicesForTest();
@@ -654,62 +641,20 @@ public class VibrationSettingsTest {
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
- // Vibrations from the primary display is never ignored regardless of the creation and
- // removal of virtual displays and of the changes of apps running on virtual displays.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
+ public void shouldIgnoreVibrationFromVirtualDevices_defaultDevice_neverIgnored() {
+ // Vibrations from the primary device is never ignored.
for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ assertVibrationNotIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT);
}
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayVirtual() {
- // Ignore the vibration when the coming display id represents a virtual display.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
-
+ public void shouldIgnoreVibrationFromVirtualDevices_virtualDevice_alwaysIgnored() {
+ // Ignore the vibration when the coming device id represents a virtual device.
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID,
+ assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID,
Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
}
-
- // Stop ignoring when the virtual display is removed.
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID);
- }
- }
-
-
- @Test
- public void shouldIgnoreVibrationFromVirtualDisplays_appsOnVirtualDisplay() {
- // Ignore when the passed-in display id is invalid and the calling uid is on a virtual
- // display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY,
- Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
- }
-
- // Stop ignoring when the app is no longer on virtual display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY);
- }
-
}
@Test
@@ -932,13 +877,13 @@ public class VibrationSettingsTest {
private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
Vibration.Status expectedStatus) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY, expectedStatus);
+ assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus);
}
- private void assertVibrationIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId, Vibration.Status expectedStatus) {
+ private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId, Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
- VibrationAttributes.createForUsage(usage), UID, displayId, null, null);
+ VibrationAttributes.createForUsage(usage), UID, deviceId, null, null);
assertEquals(errorMessageForUsage(usage), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -946,7 +891,7 @@ public class VibrationSettingsTest {
private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertEquals(errorMessageForAttributes(attrs), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -957,27 +902,27 @@ public class VibrationSettingsTest {
private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
@VibrationAttributes.Flag int flags) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, Display.DEFAULT_DISPLAY, flags);
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, Context.DEVICE_ID_DEFAULT, flags);
}
- private void assertVibrationNotIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, displayId, /* flags= */ 0);
+ private void assertVibrationNotIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId) {
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, deviceId, /* flags= */ 0);
}
- private void assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(
- @VibrationAttributes.Usage int usage, int displayId,
+ private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice(
+ @VibrationAttributes.Usage int usage, int deviceId,
@VibrationAttributes.Flag int flags) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
- displayId, null, null);
+ deviceId, null, null);
assertNull(errorMessageForUsage(usage),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertNull(errorMessageForAttributes(attrs),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -1032,7 +977,7 @@ public class VibrationSettingsTest {
private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
@VibrationAttributes.Usage int usage) {
VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
- return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DISPLAY_ID, opPkg, null);
+ return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
}
private void setBatteryReceiverRegistrationResult(Intent result) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 085241ff971a..b0aef47d0900 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -88,7 +88,7 @@ public class VibrationThreadTest {
private static final int TEST_TIMEOUT_MILLIS = 900;
private static final int UID = Process.ROOT_UID;
- private static final int DISPLAY_ID = 10;
+ private static final int DEVICE_ID = 10;
private static final int VIBRATOR_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
@@ -250,7 +250,7 @@ public class VibrationThreadTest {
Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
- 1, /* displayId= */ -1, /* opPkg= */ null, /* reason= */ null));
+ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
mVibrationConductor.notifyCancelled(
cancelVibrationInfo,
/* immediate= */ false);
@@ -1641,7 +1641,7 @@ public class VibrationThreadTest {
private HalVibration createVibration(CombinedVibration effect) {
return new HalVibration(mVibrationToken, effect,
- new Vibration.CallerInfo(ATTRS, UID, DISPLAY_ID, PACKAGE_NAME, "reason"));
+ new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
}
private SparseArray<VibratorController> createVibratorControllers() {
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 3dfaed69dea6..3fce9e7a83ef 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -84,10 +84,8 @@ import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.flags.Flags;
@@ -129,7 +127,7 @@ public class VibratorManagerServiceTest {
// be cancelled in the body of the individual test.
private static final int CLEANUP_TIMEOUT_MILLIS = 100;
private static final int UID = Process.ROOT_UID;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -190,9 +188,6 @@ public class VibratorManagerServiceTest {
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
private VibrationConfig mVibrationConfig;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
private InputManager mInputManager;
@@ -223,14 +218,6 @@ public class VibratorManagerServiceTest {
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
@@ -273,6 +260,7 @@ public class VibratorManagerServiceTest {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
// Ignore potential exceptions about the looper having never dispatched any messages.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
if (mInputManagerGlobalSession != null) {
@@ -1510,65 +1498,33 @@ public class VibratorManagerServiceTest {
}
@Test
- public void vibrate_withVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
+ public void vibrate_ignoreVibrationFromVirtualDevice() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ VIRTUAL_DEVICE_ID,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback ignored when it's from a virtual display.
+ // Haptic feedback ignored when it's from a virtual device.
assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ Context.DEVICE_ID_DEFAULT,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the virtual display is removed.
+ // Haptic feedback played normally when it's from the default device.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
cancelVibrate(service); // Clean up long-ish effect.
}
@Test
- public void vibrate_withAppsOnVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
- mockVibrators(1);
- VibratorManagerService service = createSystemReadyService();
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
-
- // Haptic feedback ignored when it's from an app running virtual display.
- assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the same app no long runs on a virtual display.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- cancelVibrate(service); // Clean up long-ish effect.
- }
-
- @Test
public void vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined()
throws Exception {
mockVibrators(1);
@@ -1685,7 +1641,7 @@ public class VibratorManagerServiceTest {
}
@Test
- public void onExternalVibration_ignoreVibrationFromVirtualDevices() throws Exception {
+ public void onExternalVibration_ignoreVibrationFromVirtualDevices() {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
createSystemReadyService();
@@ -1697,8 +1653,8 @@ public class VibratorManagerServiceTest {
int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
+ when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
+ .thenReturn(true);
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
}
@@ -2396,7 +2352,7 @@ public class VibratorManagerServiceTest {
private HalVibration performHapticFeedbackAndWaitUntilFinished(VibratorManagerService service,
int constant, boolean always) throws InterruptedException {
HalVibration vib =
- service.performHapticFeedbackInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.performHapticFeedbackInternal(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
constant, always, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2414,7 +2370,7 @@ public class VibratorManagerServiceTest {
private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
HalVibration vib =
- service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.vibrateWithPermissionCheck(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
effect, attrs, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2430,12 +2386,12 @@ public class VibratorManagerServiceTest {
private void vibrate(VibratorManagerService service, CombinedVibration effect,
VibrationAttributes attrs) {
- vibrateWithDisplay(service, Display.DEFAULT_DISPLAY, effect, attrs);
+ vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
}
- private void vibrateWithDisplay(VibratorManagerService service, int displayId,
+ private void vibrateWithDevice(VibratorManagerService service, int deviceId,
CombinedVibration effect, VibrationAttributes attrs) {
- service.vibrate(UID, displayId, PACKAGE_NAME, effect, attrs, "some reason", service);
+ service.vibrate(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service);
}
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 270d5df5e702..ab35da69da7c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,7 +46,6 @@ import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
-import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -110,8 +109,8 @@ class ShortcutKeyTestBase {
}
}
- void sendKeyCombination(int[] keyCodes, long duration, boolean longPress) {
- final long downTime = SystemClock.uptimeMillis();
+ void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress) {
+ final long downTime = mPhoneWindowManager.getCurrentTime();
final int count = keyCodes.length;
int metaState = 0;
@@ -126,14 +125,12 @@ class ShortcutKeyTestBase {
metaState |= MODIFIER.getOrDefault(keyCode, 0);
}
- try {
- Thread.sleep(duration);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
+ if (durationMillis > 0) {
+ mPhoneWindowManager.moveTimeForward(durationMillis);
}
if (longPress) {
- final long nextDownTime = SystemClock.uptimeMillis();
+ final long nextDownTime = mPhoneWindowManager.getCurrentTime();
for (int i = 0; i < count; i++) {
final int keyCode = keyCodes[i];
final KeyEvent nextDownEvent = new KeyEvent(downTime, nextDownTime,
@@ -145,7 +142,7 @@ class ShortcutKeyTestBase {
}
}
- final long eventTime = SystemClock.uptimeMillis();
+ final long eventTime = mPhoneWindowManager.getCurrentTime();
for (int i = count - 1; i >= 0; i--) {
final int keyCode = keyCodes[i];
final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode,
@@ -157,8 +154,8 @@ class ShortcutKeyTestBase {
}
}
- void sendKeyCombination(int[] keyCodes, long duration) {
- sendKeyCombination(keyCodes, duration, false /* longPress */);
+ void sendKeyCombination(int[] keyCodes, long durationMillis) {
+ sendKeyCombination(keyCodes, durationMillis, false /* longPress */);
}
void sendLongPressKeyCombination(int[] keyCodes) {
@@ -170,30 +167,7 @@ class ShortcutKeyTestBase {
}
void sendKey(int keyCode, boolean longPress) {
- final long downTime = SystemClock.uptimeMillis();
- final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode,
- 0 /*repeat*/, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
- 0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
- event.setDisplayId(DEFAULT_DISPLAY);
- interceptKey(event);
-
- if (longPress) {
- final long nextDownTime = downTime + ViewConfiguration.getLongPressTimeout();
- final KeyEvent nextDownevent = new KeyEvent(downTime, nextDownTime,
- KeyEvent.ACTION_DOWN, keyCode, 1 /*repeat*/, 0 /*metaState*/,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
- KeyEvent.FLAG_LONG_PRESS /*flags*/, InputDevice.SOURCE_KEYBOARD);
- interceptKey(nextDownevent);
- }
-
- final long eventTime = longPress
- ? SystemClock.uptimeMillis() + ViewConfiguration.getLongPressTimeout()
- : SystemClock.uptimeMillis();
- final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode,
- 0 /*repeat*/, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
- 0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
- upEvent.setDisplayId(DEFAULT_DISPLAY);
- interceptKey(upEvent);
+ sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress);
}
private void interceptKey(KeyEvent keyEvent) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index f2721a556454..7ea5010976ee 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -30,9 +30,9 @@ import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
import android.content.Context;
-import android.os.Looper;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
import android.view.KeyEvent;
@@ -109,7 +109,7 @@ public class SingleKeyGestureTests {
}
@Override
- public void onPress(long downTime) {
+ public void onPress(long downTime, int displayId) {
if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
return;
}
@@ -131,7 +131,7 @@ public class SingleKeyGestureTests {
}
@Override
- void onMultiPress(long downTime, int count) {
+ void onMultiPress(long downTime, int count, int displayId) {
if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
return;
}
@@ -141,7 +141,7 @@ public class SingleKeyGestureTests {
}
@Override
- void onKeyUp(long eventTime, int multiPressCount) {
+ void onKeyUp(long eventTime, int multiPressCount, int displayId) {
mKeyUpQueue.add(new KeyUpData(KEYCODE_POWER, multiPressCount));
}
});
@@ -159,7 +159,7 @@ public class SingleKeyGestureTests {
}
@Override
- public void onPress(long downTime) {
+ public void onPress(long downTime, int displayId) {
if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
return;
}
@@ -167,7 +167,7 @@ public class SingleKeyGestureTests {
}
@Override
- void onMultiPress(long downTime, int count) {
+ void onMultiPress(long downTime, int count, int displayId) {
if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
return;
}
@@ -177,7 +177,7 @@ public class SingleKeyGestureTests {
}
@Override
- void onKeyUp(long eventTime, int multiPressCount) {
+ void onKeyUp(long eventTime, int multiPressCount, int displayId) {
mKeyUpQueue.add(new KeyUpData(KEYCODE_BACK, multiPressCount));
}
@@ -398,7 +398,7 @@ public class SingleKeyGestureTests {
final SingleKeyGestureDetector.SingleKeyRule rule =
new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) {
@Override
- void onPress(long downTime) {
+ void onPress(long downTime, int displayId) {
mShortPressed.countDown();
}
};
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index eab8757b7331..912e1d3df945 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,15 +16,19 @@
package com.android.server.policy;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
+import android.app.ActivityManager.RecentTaskInfo;
import android.content.ComponentName;
+import android.os.RemoteException;
import android.provider.Settings;
import org.junit.Test;
@@ -120,6 +124,46 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
mPhoneWindowManager.assertStatusBarStartAssist();
}
+ @Test
+ public void stemDoubleKey_EarlyShortPress_AllAppsThenSwitchToMostRecent()
+ throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
+ int referenceId = 666;
+ recentTaskInfo.persistentId = referenceId;
+ doReturn(recentTaskInfo).when(
+ mPhoneWindowManager.mActivityTaskManagerInternal).getMostRecentTaskFromBackground();
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ }
+
+ @Test
+ public void stemDoubleKey_NoEarlyShortPress_SwitchToMostRecent() throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
+ int referenceId = 666;
+ recentTaskInfo.persistentId = referenceId;
+ doReturn(recentTaskInfo).when(
+ mPhoneWindowManager.mActivityTaskManagerInternal).getMostRecentTaskFromBackground();
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertNotOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ }
private void overrideBehavior(String key, int expectedBehavior) {
Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index e26260a6836c..7788b339738b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -57,6 +57,7 @@ import static org.mockito.Mockito.withSettings;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.SearchManager;
import android.content.ComponentName;
@@ -98,6 +99,7 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.testutils.OffsettableClock;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayPolicy;
@@ -126,7 +128,8 @@ class TestPhoneWindowManager {
@Mock private WindowManagerInternal mWindowManagerInternal;
@Mock private ActivityManagerInternal mActivityManagerInternal;
- @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ @Mock ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ @Mock IActivityManager mActivityManagerService;
@Mock private InputManagerInternal mInputManagerInternal;
@Mock private InputManager mInputManager;
@Mock private SensorPrivacyManager mSensorPrivacyManager;
@@ -160,7 +163,8 @@ class TestPhoneWindowManager {
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
- private TestLooper mTestLooper = new TestLooper();
+ private OffsettableClock mClock = new OffsettableClock();
+ private TestLooper mTestLooper = new TestLooper(() -> mClock.now());
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -181,6 +185,10 @@ class TestPhoneWindowManager {
KeyguardServiceDelegate getKeyguardServiceDelegate() {
return mKeyguardServiceDelegate;
}
+
+ IActivityManager getActivityManagerService() {
+ return mActivityManagerService;
+ }
}
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
@@ -329,6 +337,15 @@ class TestPhoneWindowManager {
mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE);
}
+ long getCurrentTime() {
+ return mClock.now();
+ }
+
+ void moveTimeForward(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
/**
* Below functions will override the setting or the policy behavior.
*/
@@ -347,6 +364,10 @@ class TestPhoneWindowManager {
mPhoneWindowManager.mShortPressOnPowerBehavior = behavior;
}
+ void overrideShouldEarlyShortPressOnStemPrimary(boolean shouldEarlyShortPress) {
+ mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress;
+ }
+
// Override assist perform function.
void overrideLongPressOnPower(int behavior) {
mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
@@ -667,4 +688,11 @@ class TestPhoneWindowManager {
vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey},
expectedModifierState), description(errorMsg));
}
+
+ void assertSwitchToRecent(int persistentId) throws RemoteException {
+ mTestLooper.dispatchAll();
+ verify(mActivityManagerService,
+ timeout(TEST_SINGLE_KEY_DELAY_MILLIS)).startActivityFromRecents(eq(persistentId),
+ isNull());
+ }
}
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 0c996e0155fd..786432a5dc88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -195,6 +195,8 @@ public class ActivityRecordTests extends WindowTestsBase {
setBooted(mAtm);
// Because the booted state is set, avoid starting real home if there is no task.
doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any());
+ // Do not execute the transaction, because we can't verify the parameter after it recycles.
+ doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
@@ -262,7 +264,7 @@ public class ActivityRecordTests extends WindowTestsBase {
pauseFound.value = true;
}
return null;
- }).when(activity.app.getThread()).scheduleTransaction(any());
+ }).when(mClientLifecycleManager).scheduleTransaction(any());
activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
@@ -477,7 +479,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.build();
final Task task = activity.getTask();
activity.setState(DESTROYED, "Testing");
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
final Configuration newConfig = new Configuration(task.getConfiguration());
newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
@@ -487,8 +489,8 @@ public class ActivityRecordTests extends WindowTestsBase {
ensureActivityConfiguration(activity);
- verify(mAtm.getLifecycleManager(), never())
- .scheduleTransaction(any(), isA(ActivityConfigurationChangeItem.class));
+ verify(mClientLifecycleManager, never())
+ .scheduleTransactionItem(any(), isA(ActivityConfigurationChangeItem.class));
}
@Test
@@ -500,7 +502,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// test properly.
activity.finishRelaunching();
// Clear out any calls to scheduleTransaction from launching the activity.
- reset(mAtm.getLifecycleManager());
+ reset(mClientLifecycleManager);
final Task task = activity.getTask();
activity.setState(RESUMED, "Testing");
@@ -517,7 +519,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// The configuration change is still sent to the activity, even if it doesn't relaunch.
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(
+ verify(mClientLifecycleManager).scheduleTransactionItem(
eq(activity.app.getThread()), eq(expected));
}
@@ -558,19 +560,7 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
activity.getConfiguration()));
- clearInvocations(mAtm.getLifecycleManager());
- final Configuration newConfig = new Configuration(activity.getConfiguration());
- final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
- final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
- if (newConfig.orientation == ORIENTATION_PORTRAIT) {
- newConfig.orientation = ORIENTATION_LANDSCAPE;
- newConfig.screenWidthDp = longSide;
- newConfig.screenHeightDp = shortSide;
- } else {
- newConfig.orientation = ORIENTATION_PORTRAIT;
- newConfig.screenWidthDp = shortSide;
- newConfig.screenHeightDp = longSide;
- }
+ clearInvocations(mClientLifecycleManager);
// Mimic the behavior that display doesn't handle app's requested orientation.
final DisplayContent dc = activity.getTask().getDisplayContent();
@@ -578,12 +568,15 @@ public class ActivityRecordTests extends WindowTestsBase {
doReturn(false).when(dc).handlesOrientationChangeFromDescendant(anyInt());
final int requestedOrientation;
- switch (newConfig.orientation) {
- case ORIENTATION_LANDSCAPE:
+ final int expectedOrientation;
+ switch (activity.getConfiguration().orientation) {
+ case ORIENTATION_PORTRAIT:
requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ expectedOrientation = ORIENTATION_LANDSCAPE;
break;
- case ORIENTATION_PORTRAIT:
+ case ORIENTATION_LANDSCAPE:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ expectedOrientation = ORIENTATION_PORTRAIT;
break;
default:
throw new IllegalStateException("Orientation in new config should be either"
@@ -595,11 +588,11 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.setRequestedOrientation(requestedOrientation);
+ final Configuration currentConfig = activity.getConfiguration();
+ assertEquals(expectedOrientation, currentConfig.orientation);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
- eq(expected));
-
+ ActivityConfigurationChangeItem.obtain(activity.token, currentConfig);
+ verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected);
verify(displayRotation).onSetRequestedOrientation();
}
@@ -788,7 +781,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
doReturn(false).when(stack).isTranslucent(any());
assertTrue(task.shouldBeVisible(null /* starting */));
@@ -796,7 +789,10 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.getConfiguration()));
final Configuration newConfig = new Configuration(activity.getConfiguration());
- final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int shortSide = newConfig.screenWidthDp == newConfig.screenHeightDp
+ // To avoid the case where it is always portrait because of width == height.
+ ? newConfig.screenWidthDp - 1
+ : Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
if (newConfig.orientation == ORIENTATION_PORTRAIT) {
newConfig.orientation = ORIENTATION_LANDSCAPE;
@@ -811,12 +807,13 @@ public class ActivityRecordTests extends WindowTestsBase {
task.onConfigurationChanged(newConfig);
activity.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */, true /* ignoreStopState */);
+ false /* preserveWindow */, true /* ignoreVisibility */);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(
- eq(activity.app.getThread()), eq(expected));
+ ActivityConfigurationChangeItem.obtain(activity.token,
+ activity.getConfiguration());
+ verify(mClientLifecycleManager).scheduleTransactionItem(
+ activity.app.getThread(), expected);
} finally {
stack.getDisplayArea().removeChild(stack);
}
@@ -1259,12 +1256,12 @@ public class ActivityRecordTests extends WindowTestsBase {
targetActivity.resultTo = sourceActivity;
targetActivity.setForceSendResultForMediaProjection();
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
try {
- verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
+ verify(mClientLifecycleManager, atLeastOnce()).scheduleTransaction(
any(ClientTransaction.class));
} catch (RemoteException ignored) {
}
@@ -1283,7 +1280,7 @@ public class ActivityRecordTests extends WindowTestsBase {
targetActivity.setState(RESUMED, "test");
targetActivity.resultTo = resultToActivity;
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
waitUntilHandlersIdle();
@@ -1786,10 +1783,10 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWithTask();
final WindowProcessController wpc = activity.app;
setup.accept(activity);
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
activity.getTask().removeImmediately("test");
try {
- verify(mAtm.getLifecycleManager()).scheduleTransaction(any(),
+ verify(mClientLifecycleManager).scheduleTransactionItem(any(),
isA(DestroyActivityItem.class));
} catch (RemoteException ignored) {
}
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 e2bb115d5fbd..98055fa6f0d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -118,6 +118,7 @@ import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
@@ -1378,7 +1379,8 @@ public class ActivityStarterTests extends WindowTestsBase {
.setUserId(10)
.build();
- final int result = starter.recycleTask(task, null, null, null);
+ final int result = starter.recycleTask(task, null, null, null,
+ BalVerdict.ALLOW_BY_DEFAULT);
assertThat(result == START_SUCCESS).isTrue();
assertThat(starter.mAddingToTask).isTrue();
}
@@ -1892,7 +1894,7 @@ public class ActivityStarterTests extends WindowTestsBase {
starter.startActivityInner(target, source, null /* voiceSession */,
null /* voiceInteractor */, 0 /* startFlags */,
options, inTask, inTaskFragment,
- BackgroundActivityStartController.BAL_ALLOW_DEFAULT, null /* intentGrants */,
- -1 /* realCallingUid */);
+ BalVerdict.ALLOW_BY_DEFAULT,
+ null /* intentGrants */, -1 /* realCallingUid */);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e7ebd7db1023..d2c731c3f8ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -39,9 +39,10 @@ import static org.junit.Assert.assertNotEquals;
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.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.never;
@@ -51,7 +52,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
-import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.EnterPipRequestedItem;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -90,9 +91,6 @@ import java.util.function.Consumer;
@RunWith(WindowTestRunner.class)
public class ActivityTaskManagerServiceTests extends WindowTestsBase {
- private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
- ArgumentCaptor.forClass(ClientTransaction.class);
-
private static final String DEFAULT_PACKAGE_NAME = "my.application.package";
private static final int DEFAULT_USER_ID = 100;
@@ -123,53 +121,42 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
doReturn(mockLifecycleManager).when(mAtm).getLifecycleManager();
doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
+ final ArgumentCaptor<ClientTransactionItem> clientTransactionItemCaptor =
+ ArgumentCaptor.forClass(ClientTransactionItem.class);
+ verify(mockLifecycleManager).scheduleTransactionItem(any(),
+ clientTransactionItemCaptor.capture());
+ final ClientTransactionItem transactionItem = clientTransactionItemCaptor.getValue();
// Check that only an enter pip request item callback was scheduled.
- assertEquals(1, transaction.getCallbacks().size());
- assertTrue(transaction.getCallbacks().get(0) instanceof EnterPipRequestedItem);
- // Check the activity lifecycle state remains unchanged.
- assertNull(transaction.getLifecycleStateRequest());
+ assertTrue(transactionItem instanceof EnterPipRequestedItem);
}
@Test
public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
doReturn(false).when(activity).inPinnedWindowingMode();
doReturn(false).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(lifecycleManager, atLeast(0))
- .scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
- // Check that none are enter pip request items.
- transaction.getCallbacks().forEach(clientTransactionItem -> {
- assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
- });
+ verify(mClientLifecycleManager, never()).scheduleTransactionItem(any(), any());
}
@Test
public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
doReturn(true).when(activity).inPinnedWindowingMode();
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(lifecycleManager, atLeast(0))
- .scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
- // Check that none are enter pip request items.
- transaction.getCallbacks().forEach(clientTransactionItem -> {
- assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
- });
+ verify(mClientLifecycleManager, never()).scheduleTransactionItem(any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 6790dc2e8733..afea8114d508 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -117,6 +117,20 @@ public class BackNavigationControllerTests extends WindowTestsBase {
}
@Test
+ public void noBackWhenMoveTaskToBack() {
+ Task taskA = createTask(mDefaultDisplay);
+ ActivityRecord recordA = createActivityRecord(taskA);
+ Mockito.doNothing().when(recordA).reparentSurfaceControl(any(), any());
+
+ final Task topTask = createTopTaskWithActivity();
+ withSystemCallback(topTask);
+ // simulate moveTaskToBack
+ topTask.setVisibleRequested(false);
+ BackNavigationInfo backNavigationInfo = startBackNavigation();
+ assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNull();
+ }
+
+ @Test
public void backTypeCrossTaskWhenBackToPreviousTask() {
Task taskA = createTask(mDefaultDisplay);
ActivityRecord recordA = createActivityRecord(taskA);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index a18dbaf39575..04aa9815e698 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -16,18 +16,32 @@
package com.android.server.wm;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+
import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Build/Install/Run:
@@ -37,23 +51,77 @@ import org.junit.Test;
@Presubmit
public class ClientLifecycleManagerTests {
+ @Mock
+ private IApplicationThread mClient;
+ @Mock
+ private IApplicationThread.Stub mNonBinderClient;
+ @Mock
+ private ClientTransactionItem mTransactionItem;
+ @Mock
+ private ActivityLifecycleItem mLifecycleItem;
+ @Captor
+ private ArgumentCaptor<ClientTransaction> mTransactionCaptor;
+
+ private ClientLifecycleManager mLifecycleManager;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mLifecycleManager = spy(new ClientLifecycleManager());
+
+ doReturn(true).when(mLifecycleItem).isActivityLifecycleItem();
+ }
+
@Test
- public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
- ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class)));
+ public void testScheduleTransaction_recycleBinderClientTransaction() throws Exception {
+ final ClientTransaction item = spy(ClientTransaction.obtain(mClient));
- ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
- clientLifecycleManager.scheduleTransaction(item);
+ mLifecycleManager.scheduleTransaction(item);
- verify(item, times(1)).recycle();
+ verify(item).recycle();
}
@Test
- public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
- ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class)));
+ public void testScheduleTransaction_notRecycleNonBinderClientTransaction() throws Exception {
+ final ClientTransaction item = spy(ClientTransaction.obtain(mNonBinderClient));
- ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
- clientLifecycleManager.scheduleTransaction(item);
+ mLifecycleManager.scheduleTransaction(item);
+
+ verify(item, never()).recycle();
+ }
+
+ @Test
+ public void testScheduleTransactionItem() throws RemoteException {
+ doNothing().when(mLifecycleManager).scheduleTransaction(any());
+ mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
+
+ verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
+ ClientTransaction transaction = mTransactionCaptor.getValue();
+ assertEquals(1, transaction.getCallbacks().size());
+ assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
+ assertNull(transaction.getLifecycleStateRequest());
+ assertNull(transaction.getTransactionItems());
+
+ clearInvocations(mLifecycleManager);
+ mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
+
+ verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
+ transaction = mTransactionCaptor.getValue();
+ assertNull(transaction.getCallbacks());
+ assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
+ }
+
+ @Test
+ public void testScheduleTransactionAndLifecycleItems() throws RemoteException {
+ doNothing().when(mLifecycleManager).scheduleTransaction(any());
+ mLifecycleManager.scheduleTransactionAndLifecycleItems(mClient, mTransactionItem,
+ mLifecycleItem);
- verify(item, times(0)).recycle();
+ verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
+ final ClientTransaction transaction = mTransactionCaptor.getValue();
+ assertEquals(1, transaction.getCallbacks().size());
+ assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
+ assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 147a44f1d297..fafc03555680 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -190,7 +190,7 @@ public class DisplayAreaPolicyTests extends WindowTestsBase {
final WindowManagerService wms = mWm;
final DisplayContent displayContent = mock(DisplayContent.class);
doReturn(true).when(displayContent).isTrusted();
- doReturn(true).when(displayContent).supportsSystemDecorations();
+ doReturn(true).when(displayContent).isHomeSupported();
final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms,
"Tasks1", FEATURE_DEFAULT_TASK_CONTAINER);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 5b88c8cbec92..c6fa8a1b5a98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -643,6 +643,21 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testDisplayHasContent() {
+ final WindowState window = createWindow(null, TYPE_APPLICATION_OVERLAY, "window");
+ setDrawnState(WindowStateAnimator.COMMIT_DRAW_PENDING, window);
+ assertFalse(mDisplayContent.getLastHasContent());
+ // The pending draw state should be committed and the has-content state is also updated.
+ mDisplayContent.applySurfaceChangesTransaction();
+ assertTrue(window.isDrawn());
+ assertTrue(mDisplayContent.getLastHasContent());
+ // If the only window is no longer visible, has-content will be false.
+ setDrawnState(WindowStateAnimator.NO_SURFACE, window);
+ mDisplayContent.applySurfaceChangesTransaction();
+ assertFalse(mDisplayContent.getLastHasContent());
+ }
+
+ @Test
public void testImeIsAttachedToDisplayForLetterboxedApp() {
final DisplayContent dc = mDisplayContent;
final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window");
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 c782d3e90e96..be96e60917a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -274,6 +274,26 @@ public class DisplayPolicyTests extends WindowTestsBase {
assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow());
}
+ @SetupWindows(addWindows = W_NOTIFICATION_SHADE)
+ @Test
+ public void testVisibleProcessWhileDozing() {
+ final WindowProcessController wpc = mNotificationShadeWindow.getProcess();
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ policy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs);
+
+ policy.screenTurnedOff();
+ policy.setAwake(false);
+ policy.screenTurnedOn(null /* screenOnListener */);
+ assertTrue(wpc.isShowingUiWhileDozing());
+ policy.screenTurnedOff();
+ assertFalse(wpc.isShowingUiWhileDozing());
+
+ policy.screenTurnedOn(null /* screenOnListener */);
+ assertTrue(wpc.isShowingUiWhileDozing());
+ policy.setAwake(true);
+ assertFalse(wpc.isShowingUiWhileDozing());
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testMainAppWindowDisallowFitSystemWindowTypes() {
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 2af67452283b..e7ac33fc6a1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -43,12 +43,9 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.RefreshCallbackItem;
import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
@@ -529,8 +526,8 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- when(mActivity.mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat())
- .thenReturn(true);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .shouldRefreshActivityViaPauseForCameraCompat();
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
@@ -571,14 +568,14 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
.setIsRefreshAfterRotationRequested(true);
- final ClientTransaction transaction = ClientTransaction.obtain(mActivity.app.getThread());
- transaction.addCallback(RefreshCallbackItem.obtain(mActivity.token,
- cycleThroughStop ? ON_STOP : ON_PAUSE));
- transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(mActivity.token,
- /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
+ final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
+ cycleThroughStop ? ON_STOP : ON_PAUSE);
+ final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+ /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
- .scheduleTransaction(eq(transaction));
+ .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(),
+ refreshCallbackItem, resumeActivityItem);
}
private void assertNoForceRotationOrRefresh() throws Exception {
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 cf620fe605c6..c404c77c8550 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -186,7 +187,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
/* options */null,
/* inTask */null,
/* inTaskFragment */ null,
- /* balCode */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
+ BalVerdict.ALLOW_BY_DEFAULT,
/* intentGrants */null,
/* realCaiingUid */ -1);
@@ -216,7 +217,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
/* options= */null,
/* inTask= */null,
/* inTaskFragment= */ null,
- /* balCode= */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
+ BalVerdict.ALLOW_BY_DEFAULT,
/* intentGrants= */null,
/* realCaiingUid */ -1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e54b8e52a4da..e2524a289b7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -496,6 +496,19 @@ public class DisplayWindowSettingsTests extends WindowTestsBase {
mPrivateDisplay.getDisplayInfo());
}
+ @Test
+ public void testClearDisplaySettings() {
+ spyOn(mWm.mDisplayWindowSettings);
+ spyOn(mWm.mDisplayWindowSettingsProvider);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ DisplayInfo info = mPrivateDisplay.getDisplayInfo();
+ wmInternal.clearDisplaySettings(info.uniqueId, info.type);
+
+ verify(mWm.mDisplayWindowSettings).clearDisplaySettings(info.uniqueId, info.type);
+ verify(mWm.mDisplayWindowSettingsProvider).clearDisplaySettings(info);
+ }
+
public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
@@ -530,5 +543,10 @@ public class DisplayWindowSettingsTests extends WindowTestsBase {
public void onDisplayRemoved(@NonNull DisplayInfo info) {
mOverrideSettingsCache.remove(info);
}
+
+ @Override
+ public void clearDisplaySettings(@NonNull DisplayInfo info) {
+ mOverrideSettingsCache.remove(info);
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 0d4c443ce1b0..c6796dc9e90d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -787,22 +787,44 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
spyOn(mController);
doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(true);
assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
}
+ @Test
+ public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(false);
+
+ assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
+ }
@Test
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
spyOn(mController);
doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(true);
assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_PORTRAIT));
}
@Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_respectOrientationReqOverUserFullScreenAndSystem() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(false);
+
+ assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
spyOn(mController);
doReturn(false).when(mController).shouldApplyUserFullscreenOverride();
@@ -872,14 +894,6 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
}
@Test
- public void testShouldApplyUserFullscreenOverride_disabledIgnoreOrientationRequest() {
- prepareActivityThatShouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(false);
-
- assertFalse(mController.shouldApplyUserFullscreenOverride());
- }
-
- @Test
public void testShouldApplyUserFullscreenOverride_returnsTrue() {
prepareActivityThatShouldApplyUserFullscreenOverride();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 491d5b56c8e2..8de45b039f62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -202,8 +202,7 @@ public class RecentsAnimationTest extends WindowTestsBase {
any() /* starting */, anyInt() /* configChanges */,
anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
- doNothing().when(lifecycleManager).scheduleTransaction(any());
+ doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
startRecentsActivity();
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 a7c14c38b832..c3102e08489d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+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.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -1301,6 +1302,27 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testShouldCreateCompatDisplayUserAspectRatioFullscreenOverride() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
+ RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ // Simulate the user selecting the fullscreen user aspect ratio override
+ spyOn(activity.mWmService.mLetterboxConfiguration);
+ spyOn(activity.mLetterboxUiController);
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(activity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
public void testNeverSandboxDisplayApis_configEnabled_sandboxingNotApplied() {
setUpDisplaySizeWithApp(1000, 1200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 4a335944228a..6655932b060b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -31,7 +31,9 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
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.verify;
import static org.mockito.Mockito.when;
@@ -143,6 +145,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
secureWindow.mAttrs.flags |= FLAG_SECURE;
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
+
+ // Verifies that if the snapshot can be cached, then getSnapshotMode should be respected.
+ // Otherwise a real snapshot can be taken even if the activity disables recents screenshot.
+ spyOn(mWm.mTaskSnapshotController);
+ final int disabledInRecentsTaskId = disabledWindow.getTask().mTaskId;
+ mAtm.takeTaskSnapshot(disabledInRecentsTaskId, true /* updateCache */);
+ verify(mWm.mTaskSnapshotController, never()).prepareTaskSnapshot(any(), any());
+ mAtm.takeTaskSnapshot(disabledInRecentsTaskId, false /* updateCache */);
+ verify(mWm.mTaskSnapshotController).prepareTaskSnapshot(any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 013be2587d76..5e531b4cbc4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -574,6 +574,7 @@ public class TaskTests extends WindowTestsBase {
spyOn(root);
spyOn(root.mLetterboxUiController);
+ doReturn(true).when(root).fillsParent();
doReturn(true).when(root.mLetterboxUiController)
.shouldEnableUserAspectRatioSettings();
doReturn(false).when(root).inSizeCompatMode();
@@ -596,6 +597,12 @@ public class TaskTests extends WindowTestsBase {
assertFalse(task.getTaskInfo()
.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton);
doReturn(false).when(root).inSizeCompatMode();
+
+ // When the top activity is transparent, the button is not enabled
+ doReturn(false).when(root).fillsParent();
+ assertFalse(task.getTaskInfo()
+ .appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton);
+ doReturn(true).when(root).fillsParent();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index a83caa4a4e95..dade3b91e0eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -636,6 +636,7 @@ public class TransitionTests extends WindowTestsBase {
transition.collect(app);
controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
null /* displayChange */);
+ assertTrue(delegateProc.isRunningRemoteTransition());
testPlayer.startTransition();
app.onStartingWindowDrawn();
// The task appeared event should be deferred until transition ready.
@@ -643,7 +644,6 @@ public class TransitionTests extends WindowTestsBase {
testPlayer.onTransactionReady(app.getSyncTransaction());
assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
- assertTrue(delegateProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
assertTrue(app.isVisible());
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 eaeb8049b81a..f99b489720b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -273,7 +273,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
assertFalse(win.mHasSurface);
assertNull(win.mWinAnimator.mSurfaceController);
- doReturn(mSystemServicesTestRule.mTransaction).when(SurfaceControl::getGlobalTransaction);
// Invisible requested activity should not get the last config even if its view is visible.
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
@@ -859,9 +858,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
final int callingUid = Process.FIRST_APPLICATION_UID;
final int callingPid = 1234;
final SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final IWindow window = mock(IWindow.class);
- final IBinder windowToken = mock(IBinder.class);
- when(window.asBinder()).thenReturn(windowToken);
+ final IBinder window = new Binder();
final IBinder focusGrantToken = mock(IBinder.class);
final InputChannel inputChannel = new InputChannel();
@@ -879,9 +876,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
final int callingUid = Process.SYSTEM_UID;
final int callingPid = 1234;
final SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final IWindow window = mock(IWindow.class);
- final IBinder windowToken = mock(IBinder.class);
- when(window.asBinder()).thenReturn(windowToken);
+ final IBinder window = new Binder();
final IBinder focusGrantToken = mock(IBinder.class);
final InputChannel inputChannel = new InputChannel();
@@ -901,9 +896,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
final int callingUid = Process.FIRST_APPLICATION_UID;
final int callingPid = 1234;
final SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final IWindow window = mock(IWindow.class);
- final IBinder windowToken = mock(IBinder.class);
- when(window.asBinder()).thenReturn(windowToken);
+ final IBinder window = new Binder();
final IBinder focusGrantToken = mock(IBinder.class);
final InputChannel inputChannel = new InputChannel();
@@ -927,9 +920,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
final int callingUid = Process.SYSTEM_UID;
final int callingPid = 1234;
final SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final IWindow window = mock(IWindow.class);
- final IBinder windowToken = mock(IBinder.class);
- when(window.asBinder()).thenReturn(windowToken);
+ final IBinder window = new Binder();
final IBinder focusGrantToken = mock(IBinder.class);
final InputChannel inputChannel = new InputChannel();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 1494f94f2052..28e0c6b65599 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1337,8 +1337,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Since we have a window we have to wait for it to draw to finish sync.
verify(mockCallback, never()).onTransactionReady(anyInt(), any());
- assertTrue(w1.useBLASTSync());
- assertTrue(w2.useBLASTSync());
+ assertTrue(w1.syncNextBuffer());
+ assertTrue(w2.syncNextBuffer());
// Make second (bottom) ready. If we started with the top, since activities fillsParent
// by default, the sync would be considered finished.
@@ -1348,16 +1348,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(SYNC_STATE_READY, w2.mSyncState);
// Even though one Window finished drawing, both windows should still be using blast sync
- assertTrue(w1.useBLASTSync());
- assertTrue(w2.useBLASTSync());
+ assertTrue(w1.syncNextBuffer());
+ assertTrue(w2.syncNextBuffer());
// A drawn window can complete the sync state automatically.
w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
makeLastConfigReportedToClient(w1, true /* visible */);
mWm.mSyncEngine.onSurfacePlacement();
verify(mockCallback).onTransactionReady(anyInt(), any());
- assertFalse(w1.useBLASTSync());
- assertFalse(w2.useBLASTSync());
+ assertFalse(w1.syncNextBuffer());
+ assertFalse(w2.syncNextBuffer());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index b89182d728ec..e152feb141e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -306,29 +306,28 @@ public class WindowProcessControllerTests extends WindowTestsBase {
@Test
public void testCachedStateConfigurationChange() throws RemoteException {
- final ClientLifecycleManager clientManager = mAtm.getLifecycleManager();
- doNothing().when(clientManager).scheduleTransaction(any(), any());
+ doNothing().when(mClientLifecycleManager).scheduleTransactionItem(any(), any());
final IApplicationThread thread = mWpc.getThread();
final Configuration newConfig = new Configuration(mWpc.getConfiguration());
newConfig.densityDpi += 100;
// Non-cached state will send the change directly.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
- clearInvocations(clientManager);
+ clearInvocations(mClientLifecycleManager);
mWpc.onConfigurationChanged(newConfig);
- verify(clientManager).scheduleTransaction(eq(thread), any());
+ verify(mClientLifecycleManager).scheduleTransactionItem(eq(thread), any());
// Cached state won't send the change.
- clearInvocations(clientManager);
+ clearInvocations(mClientLifecycleManager);
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
newConfig.densityDpi += 100;
mWpc.onConfigurationChanged(newConfig);
- verify(clientManager, never()).scheduleTransaction(eq(thread), any());
+ verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any());
// Cached -> non-cached will send the previous deferred config immediately.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
final ArgumentCaptor<ConfigurationChangeItem> captor =
ArgumentCaptor.forClass(ConfigurationChangeItem.class);
- verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
+ verify(mClientLifecycleManager).scheduleTransactionItem(eq(thread), captor.capture());
final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
captor.getValue().preExecute(client);
final ArgumentCaptor<Configuration> configCaptor =
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 014d57dd9970..d8a9a282a622 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -551,7 +551,7 @@ public class WindowStateTests extends WindowTestsBase {
final SurfaceControl.Transaction[] handledT = { null };
// The normal case that the draw transaction is applied with finishing drawing.
win.applyWithNextDraw(t -> handledT[0] = t);
- assertTrue(win.useBLASTSync());
+ assertTrue(win.syncNextBuffer());
final SurfaceControl.Transaction drawT = new StubTransaction();
final SurfaceControl.Transaction currT = win.getSyncTransaction();
clearInvocations(currT);
@@ -560,12 +560,12 @@ public class WindowStateTests extends WindowTestsBase {
// The draw transaction should be merged to current transaction even if the state is hidden.
verify(currT).merge(eq(drawT));
assertEquals(drawT, handledT[0]);
- assertFalse(win.useBLASTSync());
+ assertFalse(win.syncNextBuffer());
// If the window is gone before reporting drawn, the sync state should be cleared.
win.applyWithNextDraw(t -> handledT[0] = t);
win.destroySurfaceUnchecked();
- assertFalse(win.useBLASTSync());
+ assertFalse(win.syncNextBuffer());
assertNotEquals(drawT, handledT[0]);
}
@@ -1369,7 +1369,7 @@ public class WindowStateTests extends WindowTestsBase {
assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
// Scenario 3: test removeWindow to remove the Ime layering target overlay window.
- mWm.removeWindow(session, client);
+ mWm.removeClientToken(session, client.asBinder());
waitHandlerIdle(mWm.mH);
assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e0ed642d3130..df4af112c087 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -152,6 +152,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
ActivityTaskManagerService mAtm;
RootWindowContainer mRootWindowContainer;
ActivityTaskSupervisor mSupervisor;
+ ClientLifecycleManager mClientLifecycleManager;
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
private Session mTestSession;
@@ -215,6 +216,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
mSupervisor = mAtm.mTaskSupervisor;
mRootWindowContainer = mAtm.mRootWindowContainer;
+ mClientLifecycleManager = mAtm.getLifecycleManager();
mWm = mSystemServicesTestRule.getWindowManagerService();
mOriginalPerDisplayFocusEnabled = mWm.mPerDisplayFocusEnabled;
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f64ab22628d9..63de41f80c23 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -206,6 +206,7 @@ public class UsageStatsService extends SystemService implements
static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;
static final int MSG_UID_REMOVED = 10;
static final int MSG_USER_STARTED = 11;
+ static final int MSG_NOTIFY_USAGE_EVENT_LISTENER = 12;
private final Object mLock = new Object();
private Handler mHandler;
@@ -315,6 +316,16 @@ public class UsageStatsService extends SystemService implements
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
return true;
}
+ case MSG_NOTIFY_USAGE_EVENT_LISTENER: {
+ final int userId = msg.arg1;
+ final Event event = (Event) msg.obj;
+ synchronized (mUsageEventListeners) {
+ final int size = mUsageEventListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
+ }
+ }
+ }
}
return false;
};
@@ -532,9 +543,6 @@ public class UsageStatsService extends SystemService implements
}
reportEvent(unlockEvent, userId);
- mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
- userId, 0).sendToTarget();
-
// Remove all the stats stored in system DE.
deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
@@ -546,6 +554,8 @@ public class UsageStatsService extends SystemService implements
userService.persistActiveStats();
}
}
+
+ mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget();
}
/**
@@ -1240,12 +1250,7 @@ public class UsageStatsService extends SystemService implements
service.reportEvent(event);
}
- synchronized (mUsageEventListeners) {
- final int size = mUsageEventListeners.size();
- for (int i = 0; i < size; ++i) {
- mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
- }
- }
+ mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
}
private String getUsageSourcePackage(Event event) {
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 2975e1e050f5..fc7c6a6d0258 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -1231,6 +1231,26 @@ public class UsbPortManager implements IBinder.DeathRecipient {
complianceWarningsProto.add(FrameworkStatsLog
.USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_MISSING_RP);
continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_MISSING_DATA_LINES);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_ENUMERATION_FAIL);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_FLAKY_CONNECTION);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_UNRELIABLE_IO);
+ continue;
}
}
return complianceWarningsProto.toArray();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index bb6cf5246267..fb183754f921 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -52,9 +52,12 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA;
-import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_EGRESS_LIMIT_REACHED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_REMOTE_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
import static com.android.server.voiceinteraction.HotwordDetectionConnection.ENFORCE_HOTWORD_PHRASE_ID;
import android.annotation.NonNull;
@@ -73,7 +76,9 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
@@ -94,6 +99,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.policy.AppOpsPolicy;
import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener;
@@ -180,6 +186,13 @@ abstract class DetectorSession {
private static final int METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION =
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_STATUS_REPORTED_EXCEPTION;
+ private static final int HOTWORD_EVENT_TYPE_DETECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+ private static final int HOTWORD_EVENT_TYPE_REJECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+ private static final int HOTWORD_EVENT_TYPE_TRAINING_DATA =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
+
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
final ScheduledExecutorService mScheduledExecutorService;
@@ -516,6 +529,7 @@ abstract class DetectorSession {
if (result != null) {
Slog.i(TAG, "Egressed 'hotword rejected result' "
+ "from hotword trusted process");
+ logEgressSizeStats(result);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + result);
}
@@ -608,6 +622,7 @@ abstract class DetectorSession {
Slog.i(TAG, "Egressed "
+ HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG,
"Egressed detected result: " + newResult);
@@ -624,6 +639,32 @@ abstract class DetectorSession {
mVoiceInteractionServiceUid);
}
+ void logEgressSizeStats(HotwordTrainingData data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_TRAINING_DATA);
+ }
+
+ void logEgressSizeStats(HotwordDetectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_DETECTION);
+
+ }
+
+ void logEgressSizeStats(HotwordRejectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_REJECTION);
+ }
+
+ /** Logs event size stats for events egressed from trusted hotword detection service. */
+ private void logEgressSizeStats(Parcelable data, int eventType) {
+ BackgroundThread.getExecutor().execute(() -> {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeValue(data);
+ int dataSizeBytes = parcel.dataSize();
+ parcel.recycle();
+
+ HotwordMetricsLogger.writeHotwordDataEgressSize(eventType, dataSizeBytes,
+ getDetectorType(), mVoiceInteractionServiceUid);
+ });
+ }
+
/** Used to send training data.
*
* @hide
@@ -723,6 +764,7 @@ abstract class DetectorSession {
mVoiceInteractionServiceUid);
throw e;
}
+ logEgressSizeStats(data);
}
void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 6418f3e83114..2938a58267d7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -186,6 +186,7 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
+ logEgressSizeStats(newResult);
}
}
@@ -228,6 +229,7 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
if (mDebugHotwordLogging && result != null) {
Slog.i(TAG, "Egressed rejected result: " + result);
}
+ logEgressSizeStats(result);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
index f7b66a26ff68..ca72c85af6af 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
@@ -34,6 +34,9 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENT
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
import android.content.Context;
@@ -120,6 +123,16 @@ public final class HotwordMetricsLogger {
}
/**
+ * Logs hotword event egress size metrics.
+ */
+ public static void writeHotwordDataEgressSize(int eventType, long eventSize, int detectorType,
+ int uid) {
+ int metricsDetectorType = getHotwordEventEgressSizeDetectorType(detectorType);
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_EGRESS_SIZE_ATOM_REPORTED,
+ eventType, eventSize, metricsDetectorType, uid);
+ }
+
+ /**
* Starts a {@link LatencyTracker} log for the time it takes to show the
* {@link android.service.voice.VoiceInteractionSession} system UI after a voice trigger.
*
@@ -224,4 +237,15 @@ public final class HotwordMetricsLogger {
return AUDIO_EGRESS_NORMAL_DETECTOR;
}
}
+
+ private static int getHotwordEventEgressSizeDetectorType(int detectorType) {
+ switch (detectorType) {
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+ default:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index 2e23eff7a179..9de7f9ad4922 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -179,6 +179,7 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
}
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
@@ -194,6 +195,7 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED,
mVoiceInteractionServiceUid);
+ logEgressSizeStats(result);
// onRejected isn't allowed here, and we are not expecting it.
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a584fc9b2216..b77596391be1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2437,8 +2437,6 @@ public class VoiceInteractionManagerService extends SystemService {
synchronized (VoiceInteractionManagerServiceStub.this) {
Slog.i(TAG, "Force stopping current voice recognizer: "
+ getCurRecognizer(userHandle));
- // TODO: Figure out why the interactor was being cleared and document it.
- setCurInteractor(null, userHandle);
initRecognizer(userHandle);
}
}
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
index b1a7d819cd17..8c6e101f2c03 100644
--- a/telecomm/java/android/telecom/CallAttributes.java
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -24,6 +24,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -83,6 +85,7 @@ public final class CallAttributes implements Parcelable {
/** @hide */
@IntDef(value = {DIRECTION_INCOMING, DIRECTION_OUTGOING})
+ @Retention(RetentionPolicy.SOURCE)
public @interface Direction {
}
/**
@@ -96,6 +99,7 @@ public final class CallAttributes implements Parcelable {
/** @hide */
@IntDef(value = {AUDIO_CALL, VIDEO_CALL})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallType {
}
/**
@@ -110,6 +114,7 @@ public final class CallAttributes implements Parcelable {
/** @hide */
@IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallCapability {
}
/**
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 3158ad8fc58e..287aa653ef9a 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -7,10 +7,12 @@ rgreenwalt@google.com
tgunn@google.com
huiwang@google.com
jayachandranc@google.com
-chinmayd@google.com
amruthr@google.com
sasindran@google.com
# Requiring TL ownership for new carrier config keys.
per-file CarrierConfigManager.java=set noparent
per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
+
+#Domain Selection is jointly owned, add additional owners for domain selection specific files
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
index f7c8237bdda4..cc8a992c45e2 100644
--- a/telephony/java/android/service/euicc/EuiccProfileInfo.java
+++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java
@@ -43,7 +43,11 @@ import java.util.Objects;
@SystemApi
public final class EuiccProfileInfo implements Parcelable {
- /** Profile policy rules (bit mask) */
+ /**
+ * Profile policy rules (bit mask)
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "POLICY_RULE_" }, value = {
POLICY_RULE_DO_NOT_DISABLE,
@@ -58,7 +62,11 @@ public final class EuiccProfileInfo implements Parcelable {
/** This profile should be deleted after being disabled. */
public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 1 << 2;
- /** Class of the profile */
+ /**
+ * Class of the profile
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "PROFILE_CLASS_" }, value = {
PROFILE_CLASS_TESTING,
@@ -79,7 +87,11 @@ public final class EuiccProfileInfo implements Parcelable {
*/
public static final int PROFILE_CLASS_UNSET = -1;
- /** State of the profile */
+ /**
+ * State of the profile
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "PROFILE_STATE_" }, value = {
PROFILE_STATE_DISABLED,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index baeff06a5836..c124079ca2e3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8875,6 +8875,19 @@ public class CarrierConfigManager {
KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
/**
+ * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+ * of child session.
+ * Possible values are:
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+ KEY_PREFIX + "child_session_aes_gcm_key_size_int_array";
+
+ /**
* List of supported encryption algorithms for child session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
@@ -8883,6 +8896,16 @@ public class CarrierConfigManager {
KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
/**
+ * List of supported AEAD algorithms for child session. Possible values are
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+ KEY_PREFIX + "supported_child_session_aead_algorithms_int_array";
+
+ /**
* Time in seconds after which the IKE session is terminated if rekey procedure is not
* successful. If not set or set to <= 0, default value is 3600 seconds.
*/
@@ -8919,6 +8942,19 @@ public class CarrierConfigManager {
KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
/**
+ * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+ * of IKE session.
+ * Possible values -
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+ KEY_PREFIX + "ike_session_encryption_aes_gcm_key_size_int_array";
+
+ /**
* List of supported encryption algorithms for IKE session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
@@ -8927,6 +8963,16 @@ public class CarrierConfigManager {
KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
/**
+ * List of supported AEAD algorithms for IKE session. Possible values are
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+ KEY_PREFIX + "supported_ike_session_aead_algorithms_int_array";
+
+ /**
* List of supported integrity algorithms for IKE session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
* {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
@@ -9156,9 +9202,13 @@ public class CarrierConfigManager {
KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
+ KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
+ KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
new int[] {
SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
@@ -9207,6 +9257,10 @@ public class CarrierConfigManager {
SaProposal.KEY_LEN_AES_192,
SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
+ KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
+ KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
defaults.putIntArray(
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
index b00c57351589..71df1f2fa28c 100644
--- a/telephony/java/android/telephony/NumberVerificationCallback.java
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -20,6 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A callback for number verification. After a request for number verification is received,
* the system will call {@link #onCallReceived(String)} if a phone call was received from a number
@@ -34,6 +37,7 @@ public interface NumberVerificationCallback {
REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
REASON_IN_EMERGENCY_CALL},
prefix = {"REASON_"})
+ @Retention(RetentionPolicy.SOURCE)
@interface NumberVerificationFailureReason {}
/**
diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java
index b8c1ffe58371..14713c7d4b12 100644
--- a/telephony/java/android/telephony/PinResult.java
+++ b/telephony/java/android/telephony/PinResult.java
@@ -25,6 +25,8 @@ import android.os.Parcelable;
import com.android.internal.telephony.PhoneConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -46,6 +48,7 @@ public final class PinResult implements Parcelable {
PIN_RESULT_TYPE_FAILURE,
PIN_RESULT_TYPE_ABORTED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface PinResultType {}
/**
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 7f1c14b877d2..b568f07135b8 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,6 +16,8 @@
package android.telephony;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -37,8 +39,11 @@ import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.Qos;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -66,6 +71,53 @@ public final class PreciseDataConnectionState implements Parcelable {
private final LinkProperties mLinkProperties;
private final ApnSetting mApnSetting;
private final Qos mDefaultQos;
+ private final @NetworkValidationStatus int mNetworkValidationStatus;
+
+ /** @hide */
+ @IntDef(prefix = "NETWORK_VALIDATION_", value = {
+ NETWORK_VALIDATION_UNSUPPORTED,
+ NETWORK_VALIDATION_NOT_REQUESTED,
+ NETWORK_VALIDATION_IN_PROGRESS,
+ NETWORK_VALIDATION_SUCCESS,
+ NETWORK_VALIDATION_FAILURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkValidationStatus {}
+
+ /**
+ * Unsupported. The unsupported state is used when the data network cannot support the network
+ * validation function for the current data connection state.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
+
+ /**
+ * Not Requested. The not requested status is used when the data network supports the network
+ * validation function, but no network validation is being performed yet.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
+
+ /**
+ * In progress. The in progress state is used when the network validation process for the data
+ * network is in progress. This state is followed by either success or failure.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
+
+ /**
+ * Success. The Success status is used when network validation has been completed for the data
+ * network and the result is successful.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public static final int NETWORK_VALIDATION_SUCCESS = 3;
+
+ /**
+ * Failure. The Failure status is used when network validation has been completed for the data
+ * network and the result is failure.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public static final int NETWORK_VALIDATION_FAILURE = 4;
/**
* Constructor
@@ -87,7 +139,7 @@ public final class PreciseDataConnectionState implements Parcelable {
.setApnTypeBitmask(apnTypes)
.setApnName(apn)
.setEntryName(apn)
- .build(), null);
+ .build(), null, NETWORK_VALIDATION_UNSUPPORTED);
}
@@ -109,7 +161,8 @@ public final class PreciseDataConnectionState implements Parcelable {
private PreciseDataConnectionState(@TransportType int transportType, int id,
@DataState int state, @NetworkType int networkType,
@Nullable LinkProperties linkProperties, @DataFailureCause int failCause,
- @Nullable ApnSetting apnSetting, @Nullable Qos defaultQos) {
+ @Nullable ApnSetting apnSetting, @Nullable Qos defaultQos,
+ @NetworkValidationStatus int networkValidationStatus) {
mTransportType = transportType;
mId = id;
mState = state;
@@ -118,6 +171,7 @@ public final class PreciseDataConnectionState implements Parcelable {
mFailCause = failCause;
mApnSetting = apnSetting;
mDefaultQos = defaultQos;
+ mNetworkValidationStatus = networkValidationStatus;
}
/**
@@ -140,6 +194,7 @@ public final class PreciseDataConnectionState implements Parcelable {
mDefaultQos = in.readParcelable(
Qos.class.getClassLoader(),
android.telephony.data.Qos.class);
+ mNetworkValidationStatus = in.readInt();
}
/**
@@ -289,6 +344,16 @@ public final class PreciseDataConnectionState implements Parcelable {
return mDefaultQos;
}
+ /**
+ * Returns the network validation state.
+ *
+ * @return the network validation status of the data call
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public @NetworkValidationStatus int getNetworkValidationStatus() {
+ return mNetworkValidationStatus;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -304,6 +369,7 @@ public final class PreciseDataConnectionState implements Parcelable {
out.writeInt(mFailCause);
out.writeParcelable(mApnSetting, flags);
out.writeParcelable(mDefaultQos, flags);
+ out.writeInt(mNetworkValidationStatus);
}
public static final @NonNull Parcelable.Creator<PreciseDataConnectionState> CREATOR
@@ -321,7 +387,7 @@ public final class PreciseDataConnectionState implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mTransportType, mId, mState, mNetworkType, mFailCause,
- mLinkProperties, mApnSetting, mDefaultQos);
+ mLinkProperties, mApnSetting, mDefaultQos, mNetworkValidationStatus);
}
@@ -337,7 +403,8 @@ public final class PreciseDataConnectionState implements Parcelable {
&& mFailCause == that.mFailCause
&& Objects.equals(mLinkProperties, that.mLinkProperties)
&& Objects.equals(mApnSetting, that.mApnSetting)
- && Objects.equals(mDefaultQos, that.mDefaultQos);
+ && Objects.equals(mDefaultQos, that.mDefaultQos)
+ && mNetworkValidationStatus == that.mNetworkValidationStatus;
}
@NonNull
@@ -354,11 +421,34 @@ public final class PreciseDataConnectionState implements Parcelable {
sb.append(", link properties: " + mLinkProperties);
sb.append(", default QoS: " + mDefaultQos);
sb.append(", fail cause: " + DataFailCause.toString(mFailCause));
+ sb.append(", network validation status: "
+ + networkValidationStatusToString(mNetworkValidationStatus));
return sb.toString();
}
/**
+ * Convert a network validation status to string.
+ *
+ * @param networkValidationStatus network validation status.
+ * @return string of validation status.
+ *
+ * @hide
+ */
+ @NonNull
+ public static String networkValidationStatusToString(
+ @NetworkValidationStatus int networkValidationStatus) {
+ switch (networkValidationStatus) {
+ case NETWORK_VALIDATION_UNSUPPORTED: return "unsupported";
+ case NETWORK_VALIDATION_NOT_REQUESTED: return "not requested";
+ case NETWORK_VALIDATION_IN_PROGRESS: return "in progress";
+ case NETWORK_VALIDATION_SUCCESS: return "success";
+ case NETWORK_VALIDATION_FAILURE: return "failure";
+ default: return Integer.toString(networkValidationStatus);
+ }
+ }
+
+ /**
* {@link PreciseDataConnectionState} builder
*
* @hide
@@ -394,6 +484,10 @@ public final class PreciseDataConnectionState implements Parcelable {
/** The Default QoS for this EPS/5GS bearer or null otherwise */
private @Nullable Qos mDefaultQos;
+ /** The network validation status for the data connection. */
+ private @NetworkValidationStatus int mNetworkValidationStatus =
+ NETWORK_VALIDATION_UNSUPPORTED;
+
/**
* Set the transport type of the data connection.
*
@@ -486,13 +580,27 @@ public final class PreciseDataConnectionState implements Parcelable {
}
/**
+ * Set the network validation state for the data connection.
+ *
+ * @param networkValidationStatus the network validation status of the data call
+ * @return The builder
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public @NonNull Builder setNetworkValidationStatus(
+ @NetworkValidationStatus int networkValidationStatus) {
+ mNetworkValidationStatus = networkValidationStatus;
+ return this;
+ }
+
+ /**
* Build the {@link PreciseDataConnectionState} instance.
*
* @return The {@link PreciseDataConnectionState} instance
*/
public PreciseDataConnectionState build() {
return new PreciseDataConnectionState(mTransportType, mId, mState, mNetworkType,
- mLinkProperties, mFailCause, mApnSetting, mDefaultQos);
+ mLinkProperties, mFailCause, mApnSetting, mDefaultQos,
+ mNetworkValidationStatus);
}
}
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 3991bb20e477..60cc9c7f73cf 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -18,6 +18,7 @@ package android.telephony;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -50,6 +51,7 @@ import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.SmsRawData;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -2815,6 +2817,7 @@ public final class SmsManager {
* <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
* <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
* <code>MMS_ERROR_DATA_DISABLED</code><br>
+ * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
* @throws IllegalArgumentException if contentUri is empty
*/
public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
@@ -2856,6 +2859,7 @@ public final class SmsManager {
* <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
* <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
* <code>MMS_ERROR_DATA_DISABLED</code><br>
+ * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
* @param messageId an id that uniquely identifies the message requested to be sent.
* Used for logging and diagnostics purposes. The id may be 0.
* @throws IllegalArgumentException if contentUri is empty
@@ -2916,6 +2920,7 @@ public final class SmsManager {
* <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
* <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
* <code>MMS_ERROR_DATA_DISABLED</code><br>
+ * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
@@ -2959,6 +2964,7 @@ public final class SmsManager {
* <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
* <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
* <code>MMS_ERROR_DATA_DISABLED</code><br>
+ * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
* @param messageId an id that uniquely identifies the message requested to be downloaded.
* Used for logging and diagnostics purposes. The id may be 0.
* @throws IllegalArgumentException if locationUrl or contentUri is empty
@@ -3028,7 +3034,7 @@ public final class SmsManager {
public static final int MMS_ERROR_CONFIGURATION_ERROR = 7;
/**
- * There is no data network.
+ * There is neither Wi-Fi nor mobile data network.
*/
public static final int MMS_ERROR_NO_DATA_NETWORK = 8;
@@ -3047,6 +3053,12 @@ public final class SmsManager {
*/
public static final int MMS_ERROR_DATA_DISABLED = 11;
+ /**
+ * MMS is disabled by a carrier.
+ */
+ @FlaggedApi(Flags.FLAG_MMS_DISABLED_ERROR)
+ public static final int MMS_ERROR_MMS_DISABLED_BY_CARRIER = 12;
+
/** Intent extra name for MMS sending result data in byte array type */
public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
/** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1c5761dcebe0..b96914e59c0e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3400,6 +3400,7 @@ public class TelephonyManager {
SIM_STATE_LOADED,
SIM_STATE_PRESENT,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SimState {}
/**
@@ -10170,6 +10171,7 @@ public class TelephonyManager {
CALL_COMPOSER_STATUS_ON,
CALL_COMPOSER_STATUS_OFF,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallComposerStatus {}
/**
@@ -13157,7 +13159,7 @@ public class TelephonyManager {
CARRIER_RESTRICTION_STATUS_RESTRICTED,
CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER
})
-
+ @Retention(RetentionPolicy.SOURCE)
public @interface CarrierRestrictionStatus {
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 4b1a72695e50..3e8787281f85 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -951,8 +951,8 @@ public class ApnSetting implements Parcelable {
* See 3GPP TS 23.501 section 5.6.13
*
* @return True if the PDU session for this APN should always be on and false otherwise
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public boolean isAlwaysOn() {
return mAlwaysOn;
}
@@ -2282,9 +2282,9 @@ public class ApnSetting implements Parcelable {
* See 3GPP TS 23.501 section 5.6.13
*
* @param alwaysOn the always on status to set for this APN
- * @hide
*/
- public Builder setAlwaysOn(boolean alwaysOn) {
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
+ public @NonNull Builder setAlwaysOn(boolean alwaysOn) {
this.mAlwaysOn = alwaysOn;
return this;
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index c7f0c5f753db..9dd83d1438e2 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -17,6 +17,7 @@
package android.telephony.data;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -27,9 +28,11 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.DataFailCause;
+import android.telephony.PreciseDataConnectionState;
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -123,7 +126,6 @@ public final class DataCallResponse implements Parcelable {
* Indicates that the pdu session id is not set.
*/
public static final int PDU_SESSION_ID_NOT_SET = 0;
-
private final @DataFailureCause int mCause;
private final long mSuggestedRetryTime;
private final int mId;
@@ -143,6 +145,7 @@ public final class DataCallResponse implements Parcelable {
private final List<QosBearerSession> mQosBearerSessions;
private final NetworkSliceInfo mSliceInfo;
private final List<TrafficDescriptor> mTrafficDescriptors;
+ private final @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -185,7 +188,8 @@ public final class DataCallResponse implements Parcelable {
HANDOVER_FAILURE_MODE_LEGACY, PDU_SESSION_ID_NOT_SET,
null /* defaultQos */, Collections.emptyList() /* qosBearerSessions */,
null /* sliceInfo */,
- Collections.emptyList() /* trafficDescriptors */);
+ Collections.emptyList(), /* trafficDescriptors */
+ PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -196,7 +200,8 @@ public final class DataCallResponse implements Parcelable {
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
@Nullable Qos defaultQos, @NonNull List<QosBearerSession> qosBearerSessions,
@Nullable NetworkSliceInfo sliceInfo,
- @NonNull List<TrafficDescriptor> trafficDescriptors) {
+ @NonNull List<TrafficDescriptor> trafficDescriptors,
+ @PreciseDataConnectionState.NetworkValidationStatus int networkValidationStatus) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -216,6 +221,7 @@ public final class DataCallResponse implements Parcelable {
mQosBearerSessions = new ArrayList<>(qosBearerSessions);
mSliceInfo = sliceInfo;
mTrafficDescriptors = new ArrayList<>(trafficDescriptors);
+ mNetworkValidationStatus = networkValidationStatus;
if (mLinkStatus == LINK_STATUS_ACTIVE
|| mLinkStatus == LINK_STATUS_DORMANT) {
@@ -270,6 +276,7 @@ public final class DataCallResponse implements Parcelable {
source.readList(mTrafficDescriptors,
TrafficDescriptor.class.getClassLoader(),
android.telephony.data.TrafficDescriptor.class);
+ mNetworkValidationStatus = source.readInt();
}
/**
@@ -442,6 +449,17 @@ public final class DataCallResponse implements Parcelable {
return Collections.unmodifiableList(mTrafficDescriptors);
}
+ /**
+ * Return the network validation status that was initiated by {@link
+ * DataService.DataServiceProvider#requestValidation}
+ *
+ * @return The network validation status of data connection.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
+ return mNetworkValidationStatus;
+ }
+
@NonNull
@Override
public String toString() {
@@ -466,6 +484,8 @@ public final class DataCallResponse implements Parcelable {
.append(" qosBearerSessions=").append(mQosBearerSessions)
.append(" sliceInfo=").append(mSliceInfo)
.append(" trafficDescriptors=").append(mTrafficDescriptors)
+ .append(" networkValidationStatus=").append(PreciseDataConnectionState
+ .networkValidationStatusToString(mNetworkValidationStatus))
.append("}");
return sb.toString();
}
@@ -504,7 +524,8 @@ public final class DataCallResponse implements Parcelable {
&& mQosBearerSessions.containsAll(other.mQosBearerSessions) // non-null
&& Objects.equals(mSliceInfo, other.mSliceInfo)
&& mTrafficDescriptors.size() == other.mTrafficDescriptors.size() // non-null
- && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); // non-null
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors) // non-null
+ && mNetworkValidationStatus == other.mNetworkValidationStatus;
}
@Override
@@ -513,7 +534,7 @@ public final class DataCallResponse implements Parcelable {
mInterfaceName, Set.copyOf(mAddresses), Set.copyOf(mDnsAddresses),
Set.copyOf(mGatewayAddresses), Set.copyOf(mPcscfAddresses), mMtu, mMtuV4, mMtuV6,
mHandoverFailureMode, mPduSessionId, mDefaultQos, Set.copyOf(mQosBearerSessions),
- mSliceInfo, Set.copyOf(mTrafficDescriptors));
+ mSliceInfo, Set.copyOf(mTrafficDescriptors), mNetworkValidationStatus);
}
@Override
@@ -542,6 +563,7 @@ public final class DataCallResponse implements Parcelable {
dest.writeList(mQosBearerSessions);
dest.writeParcelable(mSliceInfo, flags);
dest.writeList(mTrafficDescriptors);
+ dest.writeInt(mNetworkValidationStatus);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -629,6 +651,9 @@ public final class DataCallResponse implements Parcelable {
private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+ private @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus =
+ PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;
+
/**
* Default constructor for Builder.
*/
@@ -905,6 +930,20 @@ public final class DataCallResponse implements Parcelable {
}
/**
+ * Set the network validation status that corresponds to the state of the network validation
+ * request started by {@link DataService.DataServiceProvider#requestValidation}
+ *
+ * @param status The network validation status.
+ * @return The same instance of the builder.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public @NonNull Builder setNetworkValidationStatus(
+ @PreciseDataConnectionState.NetworkValidationStatus int status) {
+ mNetworkValidationStatus = status;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -913,7 +952,8 @@ public final class DataCallResponse implements Parcelable {
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
+ mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors,
+ mNetworkValidationStatus);
}
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index d8b2cbebdf28..80e91a330185 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -16,6 +16,8 @@
package android.telephony.data;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -26,6 +28,7 @@ import android.app.Service;
import android.content.Intent;
import android.net.LinkProperties;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -36,6 +39,9 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -44,6 +50,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Base class of data service. Services that extend DataService must register the service in
@@ -113,11 +121,14 @@ public abstract class DataService extends Service {
private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED = 14;
private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED = 15;
private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED = 16;
+ private static final int DATA_SERVICE_REQUEST_VALIDATION = 17;
private final HandlerThread mHandlerThread;
private final DataServiceHandler mHandler;
+ private final Executor mHandlerExecutor;
+
private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
/** @hide */
@@ -379,6 +390,43 @@ public abstract class DataService extends Service {
}
}
+ /**
+ * Request validation check to see if the network is working properly for a given data call.
+ *
+ * <p>This request is completed immediately after submitting the request to the data service
+ * provider and receiving {@link DataServiceCallback.ResultCode}, and progress status or
+ * validation results are notified through {@link
+ * DataCallResponse#getNetworkValidationStatus}.
+ *
+ * <p> If the network validation request is submitted successfully, {@link
+ * DataServiceCallback#RESULT_SUCCESS} is passed to {@code resultCodeCallback}. If the
+ * network validation feature is not supported by the data service provider itself, {@link
+ * DataServiceCallback#RESULT_ERROR_UNSUPPORTED} is passed to {@code resultCodeCallback}.
+ * See {@link DataServiceCallback.ResultCode} for the type of response that indicates
+ * whether the request was successfully submitted or had an error.
+ *
+ * <p>In response to this network validation request, providers can validate the data call
+ * in their own way. For example, in IWLAN, the DPD (Dead Peer Detection) can be used as a
+ * tool to check whether a data call is alive.
+ *
+ * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
+ * @param executor The callback executor for the response.
+ * @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
+ * request validation to the DataService and checks if the request has been submitted.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public void requestValidation(int cid,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
+ Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
+
+ Log.d(TAG, "requestValidation: " + cid);
+
+ // The default implementation is to return unsupported.
+ executor.execute(() -> resultCodeCallback
+ .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
+ }
/**
* Notify the system that current data call list changed. Data service must invoke this
@@ -537,6 +585,17 @@ public abstract class DataService extends Service {
}
}
+ private static final class ValidationRequest {
+ public final int cid;
+ public final Executor executor;
+ public final IIntegerConsumer callback;
+ ValidationRequest(int cid, Executor executor, IIntegerConsumer callback) {
+ this.cid = cid;
+ this.executor = executor;
+ this.callback = callback;
+ }
+ }
+
private class DataServiceHandler extends Handler {
DataServiceHandler(Looper looper) {
@@ -679,6 +738,15 @@ public abstract class DataService extends Service {
loge("Failed to call onApnUnthrottled. " + e);
}
break;
+ case DATA_SERVICE_REQUEST_VALIDATION:
+ if (serviceProvider == null) break;
+ ValidationRequest validationRequest = (ValidationRequest) message.obj;
+ serviceProvider.requestValidation(
+ validationRequest.cid,
+ validationRequest.executor,
+ FunctionalUtils
+ .ignoreRemoteException(validationRequest.callback::accept));
+ break;
}
}
}
@@ -691,6 +759,7 @@ public abstract class DataService extends Service {
mHandlerThread.start();
mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+ mHandlerExecutor = new HandlerExecutor(mHandler);
log("Data service created");
}
@@ -853,6 +922,18 @@ public abstract class DataService extends Service {
mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED,
slotIndex, 0, callback).sendToTarget();
}
+
+ @Override
+ public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) {
+ if (resultCodeCallback == null) {
+ loge("requestValidation: resultCodeCallback is null");
+ return;
+ }
+ ValidationRequest validationRequest =
+ new ValidationRequest(cid, mHandlerExecutor, resultCodeCallback);
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_VALIDATION,
+ slotIndex, 0, validationRequest).sendToTarget();
+ }
}
private void log(String s) {
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 134694694a0e..15f88815ec6b 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -22,6 +22,8 @@ import android.telephony.data.IDataServiceCallback;
import android.telephony.data.NetworkSliceInfo;
import android.telephony.data.TrafficDescriptor;
+import com.android.internal.telephony.IIntegerConsumer;
+
/**
* {@hide}
*/
@@ -46,4 +48,5 @@ oneway interface IDataService
void cancelHandover(int slotId, int cid, IDataServiceCallback callback);
void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
+ void requestValidation(int slotId, int cid, IIntegerConsumer callback);
}
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
index 32ffdbc2121c..bdd212afd4b0 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
@@ -16,6 +16,8 @@
package android.telephony.data;
+import com.android.internal.telephony.IIntegerConsumer;
+
/**
* The qualified networks service call back interface
* @hide
@@ -23,4 +25,5 @@ package android.telephony.data;
oneway interface IQualifiedNetworksServiceCallback
{
void onQualifiedNetworkTypesChanged(int apnTypes, in int[] qualifiedNetworkTypes);
+ void onNetworkValidationRequested(int networkCapability, IIntegerConsumer callback);
}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 56f0f9f13772..c3ba09248298 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -16,6 +16,8 @@
package android.telephony.data;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -29,13 +31,23 @@ import android.os.RemoteException;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetCapability;
+import android.telephony.PreciseDataConnectionState;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Base class of the qualified networks service, which is a vendor service providing up-to-date
@@ -69,6 +81,10 @@ public abstract class QualifiedNetworksService extends Service {
private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4;
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;
+
+ /** Feature flags */
+ private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
private final HandlerThread mHandlerThread;
@@ -208,6 +224,72 @@ public abstract class QualifiedNetworksService extends Service {
}
/**
+ * Request network validation to the connected data network for given a network capability.
+ *
+ * <p>This network validation can only be performed when a data network is in connected
+ * state, and will not be triggered if the data network does not support network validation
+ * feature or network validation is not in connected state.
+ *
+ * <p>See {@link DataServiceCallback.ResultCode} for the type of response that indicates
+ * whether the request was successfully submitted or had an error.
+ *
+ * <p>If network validation is requested, monitor network validation status in {@link
+ * PreciseDataConnectionState#getNetworkValidationStatus()}.
+ *
+ * @param networkCapability A network capability. (Note that only APN-type capabilities are
+ * supported.
+ * @param executor executor The callback executor that responds whether the request has been
+ * successfully submitted or not.
+ * @param resultCodeCallback A callback to determine whether the request was successfully
+ * submitted or not.
+ */
+ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+ public void requestNetworkValidation(
+ @NetCapability int networkCapability,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
+ Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
+
+ if (!sFeatureFlag.networkValidation()) {
+ loge("networkValidation feature is disabled");
+ executor.execute(
+ () ->
+ resultCodeCallback.accept(
+ DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
+ return;
+ }
+
+ IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> resultCodeCallback.accept(result));
+ }
+ };
+
+ // Move to the internal handler and process it.
+ mHandler.obtainMessage(
+ QNS_REQUEST_NETWORK_VALIDATION,
+ mSlotIndex,
+ 0,
+ new NetworkValidationRequestData(networkCapability, callback))
+ .sendToTarget();
+ }
+
+ /** Process a network validation request on the internal handler. */
+ private void onRequestNetworkValidation(NetworkValidationRequestData data) {
+ try {
+ log("onRequestNetworkValidation");
+ // Callback to request a network validation.
+ mCallback.onNetworkValidationRequested(data.mNetworkCapability, data.mCallback);
+ } catch (RemoteException | NullPointerException e) {
+ loge("Failed to call onRequestNetworkValidation. " + e);
+ FunctionalUtils.ignoreRemoteException(data.mCallback::accept)
+ .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+ }
+
+ /**
* Called when the qualified networks provider is removed. The extended class should
* implement this method to perform cleanup works.
*/
@@ -280,6 +362,10 @@ public abstract class QualifiedNetworksService extends Service {
if (provider == null) break;
provider.onUpdateQualifiedNetworkTypes(message.arg2, (int[]) message.obj);
break;
+
+ case QNS_REQUEST_NETWORK_VALIDATION:
+ if (provider == null) break;
+ provider.onRequestNetworkValidation((NetworkValidationRequestData) message.obj);
}
}
}
@@ -364,6 +450,17 @@ public abstract class QualifiedNetworksService extends Service {
}
}
+ private static final class NetworkValidationRequestData {
+ final @NetCapability int mNetworkCapability;
+ final IIntegerConsumer mCallback;
+
+ private NetworkValidationRequestData(@NetCapability int networkCapability,
+ @NonNull IIntegerConsumer callback) {
+ mNetworkCapability = networkCapability;
+ mCallback = callback;
+ }
+ }
+
private void log(String s) {
Rlog.d(TAG, s);
}
diff --git a/telephony/java/android/telephony/data/ThrottleStatus.java b/telephony/java/android/telephony/data/ThrottleStatus.java
index 0335c6868340..0dff6ff705ca 100644
--- a/telephony/java/android/telephony/data/ThrottleStatus.java
+++ b/telephony/java/android/telephony/data/ThrottleStatus.java
@@ -27,6 +27,8 @@ import android.os.SystemClock;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -52,6 +54,7 @@ public final class ThrottleStatus implements Parcelable {
ThrottleStatus.THROTTLE_TYPE_NONE,
ThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ThrottleType {
}
@@ -76,6 +79,7 @@ public final class ThrottleStatus implements Parcelable {
ThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
ThrottleStatus.RETRY_TYPE_HANDOVER,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface RetryType {
}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 611f97b70c8c..e981e1f92071 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -67,7 +67,11 @@ import java.util.concurrent.Executor;
public class EuiccCardManager {
private static final String TAG = "EuiccCardManager";
- /** Reason for canceling a profile download session */
+ /**
+ * Reason for canceling a profile download session
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"CANCEL_REASON_"}, value = {
CANCEL_REASON_END_USER_REJECTED,
@@ -97,7 +101,11 @@ public class EuiccCardManager {
*/
public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
- /** Options for resetting eUICC memory */
+ /**
+ * Options for resetting eUICC memory
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = {"RESET_OPTION_"}, value = {
RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index b9a7d439114a..86fbb04d31b6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -552,9 +552,8 @@ public class EuiccManager {
/**
* Euicc OTA update status which can be got by {@link #getOtaStatus}
- * @hide
+ * @removed mistakenly exposed as system-api previously
*/
- @SystemApi
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"EUICC_OTA_"}, value = {
EUICC_OTA_IN_PROGRESS,
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java
index be0048f73bf4..fcc0b6ae5653 100644
--- a/telephony/java/android/telephony/euicc/EuiccNotification.java
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.java
@@ -36,7 +36,11 @@ import java.util.Objects;
*/
@SystemApi
public final class EuiccNotification implements Parcelable {
- /** Event */
+ /**
+ * Event
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "EVENT_" }, value = {
EVENT_INSTALL,
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
index 1c6b6b6e83fc..c35242d0bad8 100644
--- a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -37,7 +37,11 @@ import java.util.List;
*/
@SystemApi
public final class EuiccRulesAuthTable implements Parcelable {
- /** Profile policy rule flags */
+ /**
+ * Profile policy rule flags
+ *
+ * @removed mistakenly exposed previously
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
POLICY_RULE_FLAG_CONSENT_REQUIRED
diff --git a/telephony/java/android/telephony/ims/MediaQualityStatus.java b/telephony/java/android/telephony/ims/MediaQualityStatus.java
index 76394feaed66..e2df0d44fda8 100644
--- a/telephony/java/android/telephony/ims/MediaQualityStatus.java
+++ b/telephony/java/android/telephony/ims/MediaQualityStatus.java
@@ -24,6 +24,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.TransportType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -49,6 +51,7 @@ public final class MediaQualityStatus implements Parcelable {
MEDIA_SESSION_TYPE_AUDIO,
MEDIA_SESSION_TYPE_VIDEO,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface MediaSessionType {}
/**
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index f367e404a35b..39c9d8b94d3a 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -22,6 +22,8 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -35,6 +37,7 @@ public final class RcsClientConfiguration implements Parcelable {
/**@hide*/
@StringDef(prefix = "RCS_PROFILE_",
value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4})
+ @Retention(RetentionPolicy.SOURCE)
public @interface StringRcsProfile {}
/**
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
new file mode 100644
index 000000000000..4c37a6dc3832
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.telephony.satellite.SatelliteCapabilities;
+
+/**
+ * Interface for satellite capabilities change callback.
+ * @hide
+ */
+oneway interface ISatelliteCapabilitiesCallback {
+ /**
+ * Called when satellite capability has changed.
+ *
+ * @param capabilities The new satellite capability.
+ */
+ void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
+}
+
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
new file mode 100644
index 000000000000..b68dd5a150ff
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for satellite capabilities change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteCapabilitiesCallback {
+ /**
+ * Called when satellite capability has changed.
+ * @param capabilities The new satellite capabilities.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ void onSatelliteCapabilitiesChanged(@NonNull SatelliteCapabilities capabilities);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f18cbeafe752..71786b308937 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -81,6 +81,9 @@ public final class SatelliteManager {
new ConcurrentHashMap<>();
private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<SatelliteCapabilitiesCallback,
+ ISatelliteCapabilitiesCallback>
+ sSatelliteCapabilitiesCallbackMap = new ConcurrentHashMap<>();
private final int mSubId;
@@ -2018,7 +2021,7 @@ public final class SatelliteManager {
* {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
* </p>
*
- * @param callback The callback that was passed to
+ * @param callback The callback that was passed to.
* {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
*
* @throws SecurityException if the caller doesn't have required permission.
@@ -2046,9 +2049,84 @@ public final class SatelliteManager {
loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
ex.rethrowFromSystemServer();
}
+ }
+ /**
+ * Registers for satellite capabilities change event from the satellite service.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the satellite capabilities changed event.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SatelliteResult public int registerForSatelliteCapabilitiesChanged(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SatelliteCapabilitiesCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ISatelliteCapabilitiesCallback internalCallback =
+ new ISatelliteCapabilitiesCallback.Stub() {
+ @Override
+ public void onSatelliteCapabilitiesChanged(
+ SatelliteCapabilities capabilities) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onSatelliteCapabilitiesChanged(
+ capabilities)));
+ }
+ };
+ sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
+ return telephony.registerForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("registerForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ return SATELLITE_RESULT_REQUEST_FAILED;
}
+ /**
+ * Unregisters for satellite capabilities change event from the satellite service.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback that was passed to.
+ * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void unregisterForSatelliteCapabilitiesChanged(
+ @NonNull SatelliteCapabilitiesCallback callback) {
+ Objects.requireNonNull(callback);
+ ISatelliteCapabilitiesCallback internalCallback =
+ sSatelliteCapabilitiesCallbackMap.remove(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if (internalCallback != null) {
+ telephony.unregisterForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+ } else {
+ loge("unregisterForSatelliteCapabilitiesChanged: No internal callback.");
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("unregisterForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index d44ddfa1ee7f..ccca5adba367 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -19,6 +19,7 @@ package android.telephony.satellite.stub;
import android.telephony.satellite.stub.NtnSignalStrength;
import android.telephony.satellite.stub.NTRadioTechnology;
import android.telephony.satellite.stub.PointingInfo;
+import android.telephony.satellite.stub.SatelliteCapabilities;
import android.telephony.satellite.stub.SatelliteDatagram;
import android.telephony.satellite.stub.SatelliteModemState;
@@ -62,7 +63,15 @@ oneway interface ISatelliteListener {
/**
* Called when NTN signal strength changes.
+ *
* @param ntnSignalStrength The new NTN signal strength.
*/
void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+
+ /**
+ * Called when satellite capabilities of the satellite service have changed.
+ *
+ * @param SatelliteCapabilities The current satellite capabilities.
+ */
+ void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c212e3575276..4c53f8ab9bca 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -68,6 +68,7 @@ import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.satellite.INtnSignalStrengthCallback;
+import android.telephony.satellite.ISatelliteCapabilitiesCallback;
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -3123,4 +3124,39 @@ interface ITelephony {
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void unregisterForNtnSignalStrengthChanged(int subId,
in INtnSignalStrengthCallback callback);
+
+ /**
+ * Registers for satellite capabilities change event from the satellite service.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the satellite capabilities changed event.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int registerForSatelliteCapabilitiesChanged(int subId,
+ in ISatelliteCapabilitiesCallback callback);
+
+ /**
+ * Unregisters for satellite capabilities change event from the satellite service.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback that was passed to.
+ * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void unregisterForSatelliteCapabilitiesChanged(int subId,
+ in ISatelliteCapabilitiesCallback callback);
+
+ /**
+ * This API can be used by only CTS to override the cached value for the device overlay config
+ * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
+ * outgoing satellite datagrams should be sent to modem in demo mode.
+ *
+ * @param shouldSendToDemoMode Whether send datagram in demo mode should be sent to satellite
+ * modem or not.
+ *
+ * @return {@code true} if the operation is successful, {@code false} otherwise.
+ */
+ boolean setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index f56347fef450..50590177f791 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -544,6 +544,10 @@ public interface RILConstants {
int RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING = 243;
int RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING = 244;
int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 245;
+ int RIL_REQUEST_IS_CELLULAR_IDENTIFIER_DISCLOSED_ENABLED = 246;
+ int RIL_REQUEST_SET_CELLULAR_IDENTIFIER_DISCLOSED_ENABLED = 247;
+ int RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED = 248;
+ int RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED = 249;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -605,6 +609,8 @@ public interface RILConstants {
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
+ int RIL_UNSOL_CELLULAR_IDENTIFIER_DISCLOSED = 1056;
+ int RIL_UNSOL_SECURITY_ALGORITHMS_UPDATED = 1057;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/CtsSurfaceControlTestsStaging/OWNERS b/tests/CtsSurfaceControlTestsStaging/OWNERS
index 438b7f2ce335..8ad3446c7b52 100644
--- a/tests/CtsSurfaceControlTestsStaging/OWNERS
+++ b/tests/CtsSurfaceControlTestsStaging/OWNERS
@@ -1 +1 @@
-include platform/cts:/tests/tests/graphics/OWNERS \ No newline at end of file
+include platform/cts:/tests/tests/graphics/src/android/graphics/OWNERS \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index ae7c2a99b808..4548a7df6874 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -78,14 +78,15 @@ public class GraphicsActivity extends Activity {
// TODO(b/293651105): Unhardcode category fps range mapping
private static final FpsRange FRAME_RATE_CATEGORY_HIGH = new FpsRange(90, 120);
private static final FpsRange FRAME_RATE_CATEGORY_NORMAL = new FpsRange(60, 90);
- private static final FpsRange FRAME_RATE_CATEGORY_LOW = new FpsRange(30, 60);
+ private static final FpsRange FRAME_RATE_CATEGORY_LOW = new FpsRange(30, 30);
private DisplayManager mDisplayManager;
private SurfaceView mSurfaceView;
private Handler mHandler = new Handler(Looper.getMainLooper());
private final Object mLock = new Object();
private Surface mSurface = null;
- private float mDeviceFrameRate;
+ private float mDisplayModeRefreshRate;
+ private float mDisplayRefreshRate;
private ModeChangedEvents mModeChangedEvents = new ModeChangedEvents();
private enum ActivityState { RUNNING, PAUSED, DESTROYED }
@@ -123,14 +124,20 @@ public class GraphicsActivity extends Activity {
return;
}
synchronized (mLock) {
- Display.Mode mode = mDisplayManager.getDisplay(displayId).getMode();
+ Display display = mDisplayManager.getDisplay(displayId);
+ Display.Mode mode = display.getMode();
mModeChangedEvents.add(mode);
- float frameRate = mode.getRefreshRate();
- if (frameRate != mDeviceFrameRate) {
+ float displayModeRefreshRate = mode.getRefreshRate();
+ float displayRefreshRate = display.getRefreshRate();
+ if (displayModeRefreshRate != mDisplayModeRefreshRate
+ || displayRefreshRate != mDisplayRefreshRate) {
Log.i(TAG,
- String.format("Frame rate changed: %.2f --> %.2f", mDeviceFrameRate,
- frameRate));
- mDeviceFrameRate = frameRate;
+ String.format("Refresh rate changed: (mode) %.2f --> %.2f, "
+ + "(display) %.2f --> %.2f",
+ mDisplayModeRefreshRate, displayModeRefreshRate,
+ mDisplayRefreshRate, displayRefreshRate));
+ mDisplayModeRefreshRate = displayModeRefreshRate;
+ mDisplayRefreshRate = displayRefreshRate;
mLock.notify();
}
}
@@ -317,8 +324,10 @@ public class GraphicsActivity extends Activity {
super.onCreate(savedInstanceState);
synchronized (mLock) {
mDisplayManager = getSystemService(DisplayManager.class);
- Display.Mode mode = getDisplay().getMode();
- mDeviceFrameRate = mode.getRefreshRate();
+ Display display = getDisplay();
+ Display.Mode mode = display.getMode();
+ mDisplayModeRefreshRate = mode.getRefreshRate();
+ mDisplayRefreshRate = display.getRefreshRate();
// Insert the initial mode so we have the full display mode history.
mModeChangedEvents.add(mode);
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
@@ -516,22 +525,25 @@ public class GraphicsActivity extends Activity {
if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
// Wait until we switch to a compatible frame rate.
Log.i(TAG,
- "Verifying expected frame rate: actual (device)=" + mDeviceFrameRate
- + " expected=" + expectedFrameRate);
+ String.format(
+ "Verifying expected frame rate: actual=%.2f, expected=%.2f",
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate,
+ expectedFrameRate));
if (multiplesAllowed) {
- while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate)
+ while (!isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)
&& !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
// Empty
}
} else {
- while (!frameRateEquals(mDeviceFrameRate, expectedFrameRate)
+ while (!frameRateEquals(mDisplayRefreshRate, expectedFrameRate)
&& !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
// Empty
}
}
nowNanos = System.nanoTime();
if (nowNanos >= gracePeriodEndTimeNanos) {
- throw new FrameRateTimeoutException(expectedFrameRate, mDeviceFrameRate);
+ throw new FrameRateTimeoutException(expectedFrameRate,
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate);
}
}
@@ -541,7 +553,10 @@ public class GraphicsActivity extends Activity {
while (endTimeNanos > nowNanos) {
int numModeChangedEvents = mModeChangedEvents.size();
if (waitForEvents(endTimeNanos, surfaces)) {
- Log.i(TAG, String.format("Stable frame rate %.2f verified", mDeviceFrameRate));
+ Log.i(TAG,
+ String.format("Stable frame rate %.2f verified",
+ multiplesAllowed ? mDisplayModeRefreshRate
+ : mDisplayRefreshRate));
return;
}
nowNanos = System.nanoTime();
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index 6819a38c9067..620502e521c4 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -25,6 +25,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import org.junit.ClassRule
import org.junit.FixMethodOrder
@@ -49,21 +50,23 @@ import org.junit.runners.Parameterized
open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest) :
OpenAppFromNotificationColdTest(flicker) {
- override val openingNotificationsFromLockScreen = true
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- // Needs to run at start of transition,
- // so before the transition defined in super.transition
- transitions { device.wakeUp() }
-
- super.transition(this)
+ transitions {
+ device.wakeUp()
+ openAppFromLockNotification()
+ }
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
+ device.wakeUpAndGoToHomeScreen()
+ launchAppAndPostNotification()
+ clearOverview()
device.sleep()
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+
+ teardown { testApp.exit(wmHelper) }
}
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index 972221640363..2a458ef82448 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -49,21 +49,22 @@ import org.junit.runners.Parameterized
class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
OpenAppFromNotificationWarmTest(flicker) {
- override val openingNotificationsFromLockScreen = true
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- // Needs to run at start of transition,
- // so before the transition defined in super.transition
- transitions { device.wakeUp() }
-
- super.transition(this)
+ transitions {
+ device.wakeUp()
+ openAppFromLockNotification()
+ }
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
+ launchAppAndPostNotification()
+ goHome()
device.sleep()
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+
+ teardown { testApp.exit(wmHelper) }
}
/**
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index ffd81716e728..00aad2783d5d 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -51,21 +51,21 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker
OpenAppFromLockscreenNotificationColdTest(flicker) {
private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
- // Although we are technically still locked here, the overlay app means we should open the
- // notification shade as if we were unlocked.
- override val openingNotificationsFromLockScreen = false
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
-
transitions {
+ device.wakeUp()
+ // Although we are technically still locked here, the overlay app means we should
+ // open the
+ // notification shade as if we were unlocked.
+ openAppFromNotification()
wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
}
setup {
device.wakeUpAndGoToHomeScreen()
-
+ launchAppAndPostNotification()
+ clearOverview()
// Launch an activity that is shown when the device is locked
showWhenLockedApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withFullScreenApp(showWhenLockedApp).waitForAndVerify()
@@ -74,7 +74,10 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
- teardown { showWhenLockedApp.exit(wmHelper) }
+ teardown {
+ testApp.exit(wmHelper)
+ showWhenLockedApp.exit(wmHelper)
+ }
}
@Test
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index fbdba7b71d0f..f8d78b5ddd1e 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -23,6 +23,8 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -45,16 +47,16 @@ open class OpenAppFromNotificationColdTest(flicker: LegacyFlickerTest) :
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
-
setup {
- // Close the app that posted the notification to trigger a cold start next time
- // it is open - can't just kill it because that would remove the notification.
- tapl.setExpectedRotationCheckEnabled(false)
- tapl.goHome()
- tapl.workspace.switchToOverview()
- tapl.overview.dismissAllTasks()
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(flicker.scenario.startRotation)
+ launchAppAndPostNotification()
+ clearOverview()
}
+
+ transitions { openAppFromNotification() }
+
+ teardown { testApp.exit(wmHelper) }
}
@Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 2aa444e45ea2..5c7ec46d6f04 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -21,6 +21,7 @@ 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.FlickerTestData
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
@@ -55,63 +56,68 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) :
OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
- open val openingNotificationsFromLockScreen = false
-
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
device.wakeUpAndGoToHomeScreen()
this.setRotation(flicker.scenario.startRotation)
- testApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
- testApp.postNotification(wmHelper)
- device.pressHome()
- wmHelper
- .StateSyncBuilder()
- .withHomeActivityVisible()
- .withWindowSurfaceDisappeared(ComponentNameMatcher.NOTIFICATION_SHADE)
- .waitForAndVerify()
+ launchAppAndPostNotification()
+ goHome()
}
- transitions {
- var startY = 10
- var endY = 3 * device.displayHeight / 4
- var steps = 25
- if (openingNotificationsFromLockScreen) {
- val wm: WindowManager =
- instrumentation.context.getSystemService(WindowManager::class.java)
- ?: error("Unable to connect to WindowManager service")
- val metricInsets = wm.currentWindowMetrics.windowInsets
- val insets =
- metricInsets.getInsetsIgnoringVisibility(
- WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
- )
-
- startY = insets.top + 100
- endY = device.displayHeight / 2
- steps = 4
- }
-
- // Swipe down to show the notification shade
- val x = device.displayWidth / 2
- device.swipe(x, startY, x, endY, steps)
- device.waitForIdle(2000)
- instrumentation.uiAutomation.syncInputTransactions()
-
- // Launch the activity by clicking the notification
- val notification =
- device.wait(Until.findObject(By.text("Flicker Test Notification")), 2000L)
- notification?.click() ?: error("Notification not found")
- instrumentation.uiAutomation.syncInputTransactions()
-
- // Wait for the app to launch
- wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
- }
+ transitions { openAppFromNotification() }
teardown { testApp.exit(wmHelper) }
}
+ protected fun FlickerTestData.launchAppAndPostNotification() {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ testApp.postNotification(wmHelper)
+ }
+
+ protected fun FlickerTestData.goHome() {
+ device.pressHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(ComponentNameMatcher.NOTIFICATION_SHADE)
+ .waitForAndVerify()
+ }
+ protected fun FlickerTestData.openAppFromNotification() {
+ doOpenAppAndWait(startY = 10, endY = 3 * device.displayHeight / 4, steps = 25)
+ }
+
+ protected fun FlickerTestData.openAppFromLockNotification() {
+ val wm: WindowManager =
+ instrumentation.context.getSystemService(WindowManager::class.java)
+ ?: error("Unable to connect to WindowManager service")
+ val metricInsets = wm.currentWindowMetrics.windowInsets
+ val insets =
+ metricInsets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
+ )
+
+ doOpenAppAndWait(startY = insets.top + 100, endY = device.displayHeight / 2, steps = 4)
+ }
+
+ protected fun FlickerTestData.doOpenAppAndWait(startY: Int, endY: Int, steps: Int) {
+ // Swipe down to show the notification shade
+ val x = device.displayWidth / 2
+ device.swipe(x, startY, x, endY, steps)
+ device.waitForIdle(2000)
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Launch the activity by clicking the notification
+ val notification =
+ device.wait(Until.findObject(By.text("Flicker Test Notification")), 2000L)
+ notification?.click() ?: error("Notification not found")
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Wait for the app to launch
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ }
@Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
@Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
index 684b4b70f950..4e0e3c04ae5a 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
@@ -19,26 +19,22 @@ package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
-import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
/** Base class for app launch tests */
abstract class OpenAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit = {
- setup {
- tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(flicker.scenario.startRotation)
- }
- teardown { testApp.exit(wmHelper) }
+ protected fun clearOverview() {
+ // Close the app that posted the notification to trigger a cold start next time
+ // it is open - can't just kill it because that would remove the notification.
+ tapl.expectedRotationCheckEnabled = false
+ tapl.goHome()
+ tapl.workspace.switchToOverview()
+ tapl.overview.dismissAllTasks()
}
/**
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 93a558287bd6..c1784f3b42e7 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -149,7 +149,9 @@ class InputManagerServiceTests {
verify(native).setMotionClassifierEnabled(anyBoolean())
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
verify(native).setStylusPointerIconEnabled(anyBoolean())
- verify(native).setKeyRepeatConfiguration(anyInt(), anyInt())
+ // Called twice at boot, since there are individual callbacks to update the
+ // key repeat timeout and the key repeat delay.
+ verify(native, times(2)).setKeyRepeatConfiguration(anyInt(), anyInt())
}
@Test
diff --git a/tests/MotionPrediction/Android.bp b/tests/MotionPrediction/Android.bp
index 6cda8f050987..b4a435909953 100644
--- a/tests/MotionPrediction/Android.bp
+++ b/tests/MotionPrediction/Android.bp
@@ -26,5 +26,8 @@ package {
android_app {
name: "MotionPrediction",
srcs: ["**/*.kt"],
+ kotlincflags: [
+ "-Werror",
+ ],
sdk_version: "current",
}
diff --git a/tests/MultiDeviceInput/Android.bp b/tests/MultiDeviceInput/Android.bp
new file mode 100644
index 000000000000..3c80873168b4
--- /dev/null
+++ b/tests/MultiDeviceInput/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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 {
+ // 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_app {
+ name: "MultiDeviceInput",
+ srcs: ["**/*.kt"],
+ kotlincflags: [
+ "-Werror",
+ ],
+ sdk_version: "current",
+}
diff --git a/tests/MultiDeviceInput/AndroidManifest.xml b/tests/MultiDeviceInput/AndroidManifest.xml
new file mode 100644
index 000000000000..ed8cadb9519b
--- /dev/null
+++ b/tests/MultiDeviceInput/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.multideviceinput">
+
+ <application android:allowBackup="false"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/MultiDeviceInput/OWNERS b/tests/MultiDeviceInput/OWNERS
new file mode 100644
index 000000000000..c88bfe97cab9
--- /dev/null
+++ b/tests/MultiDeviceInput/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/tests/MultiDeviceInput/README.md b/tests/MultiDeviceInput/README.md
new file mode 100644
index 000000000000..5fcdeda6e5d7
--- /dev/null
+++ b/tests/MultiDeviceInput/README.md
@@ -0,0 +1,19 @@
+# MultiDeviceInput test app #
+
+This demo app is for manual testing of the multi-device input feature.
+It creates two windows - one on the left and one on the right. You can use different input devices
+in these windows.
+
+## Installation ##
+Install this using:
+```
+APP=MultiDeviceInput; m $APP && adb install $ANDROID_PRODUCT_OUT/system/app/$APP/$APP.apk
+```
+
+## Features ##
+
+* Touch in one window, use stylus in another window, at the same time
+* Visualize hovering stylus
+* Pinch zoom in one window to affect the line thickness in another window
+* Check whether stylus rejects touch in the same window
+* (in the future) Check stylus and touch operation in the same window
diff --git a/tests/MultiDeviceInput/res/layout/activity_main.xml b/tests/MultiDeviceInput/res/layout/activity_main.xml
new file mode 100644
index 000000000000..a6a6f891a034
--- /dev/null
+++ b/tests/MultiDeviceInput/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="test.multideviceinput.MainActivity">
+
+</LinearLayout>
diff --git a/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000000..cde69bcccec6
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..c133a0cbd379
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..bfa42f0e7b91
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..324e72cdd748
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..aee44e138434
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/values-w820dp/dimens.xml b/tests/MultiDeviceInput/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000000..b14a560efd72
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/colors.xml b/tests/MultiDeviceInput/res/values/colors.xml
new file mode 100644
index 000000000000..c37df9f7b428
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/dimens.xml b/tests/MultiDeviceInput/res/values/dimens.xml
new file mode 100644
index 000000000000..bdb8ede1c913
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/strings.xml b/tests/MultiDeviceInput/res/values/strings.xml
new file mode 100644
index 000000000000..3827c344f87f
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/strings.xml
@@ -0,0 +1,17 @@
+<!-- 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.
+-->
+<resources>
+ <string name="app_name">Simultaneous touch and stylus</string>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/styles.xml b/tests/MultiDeviceInput/res/values/styles.xml
new file mode 100644
index 000000000000..a563e7e09706
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/styles.xml
@@ -0,0 +1,23 @@
+<!-- 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.
+-->
+<resources>
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="android:colorPrimary">@color/colorPrimary</item>
+ <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="android:colorAccent">@color/colorAccent</item>
+ </style>
+</resources>
diff --git a/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt
new file mode 100644
index 000000000000..b5bd9ca746aa
--- /dev/null
+++ b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt
@@ -0,0 +1,183 @@
+/*
+ * 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 test.multideviceinput
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_HOVER_EXIT
+import android.view.MotionEvent.ACTION_UP
+import android.view.ScaleGestureDetector
+import android.view.View
+
+import java.util.Vector
+
+private fun drawLine(canvas: Canvas, from: MotionEvent, to: MotionEvent, paint: Paint) {
+ // Correct implementation here would require us to build a set of pointers and then iterate
+ // through them. Instead, we are taking a few shortcuts and ignore some of the events, which
+ // causes occasional gaps in the drawings.
+ if (from.pointerCount != to.pointerCount) {
+ return
+ }
+ // Now, 'from' is guaranteed to have as many pointers as the 'to' event. It doesn't
+ // necessarily mean they are the same pointers, though.
+ for (p in 0..<from.pointerCount) {
+ val x0 = from.getX(p)
+ val y0 = from.getY(p)
+ if (to.getPointerId(p) == from.getPointerId(p)) {
+ // This only works when the i-th pointer in "to" is the same pointer
+ // as the i-th pointer in "from"`. It's not guaranteed by the input APIs,
+ // but it works in practice.
+ val x1 = to.getX(p)
+ val y1 = to.getY(p)
+ // Ignoring historical data here for simplicity
+ canvas.drawLine(x0, y0, x1, y1, paint)
+ }
+ }
+}
+
+private fun drawCircle(canvas: Canvas, event: MotionEvent, paint: Paint, radius: Float) {
+ val x = event.getX()
+ val y = event.getY()
+ canvas.drawCircle(x, y, radius, paint)
+}
+
+/**
+ * Draw the current stroke
+ */
+class DrawingView : View {
+ private val TAG = "DrawingView"
+
+ private var myState: SharedScaledPointerSize? = null
+ private var otherState: SharedScaledPointerSize? = null
+
+ constructor(
+ context: Context,
+ myState: SharedScaledPointerSize,
+ otherState: SharedScaledPointerSize
+ ) : super(context) {
+ this.myState = myState
+ this.otherState = otherState
+ init()
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init()
+ }
+
+ val touchEvents = mutableMapOf<Int, Vector<Pair<MotionEvent, Paint>>>()
+ val hoverEvents = mutableMapOf<Int, MotionEvent>()
+
+ val scaleGestureListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
+
+ override fun onScaleBegin(scaleGestureDetector: ScaleGestureDetector): Boolean {
+ return true
+ }
+
+ override fun onScale(scaleGestureDetector: ScaleGestureDetector): Boolean {
+ val scaleFactor = scaleGestureDetector.scaleFactor
+ when (otherState?.state) {
+ PointerState.DOWN -> {
+ otherState?.lineSize = (otherState?.lineSize ?: 5f) * scaleFactor
+ }
+ PointerState.HOVER -> {
+ otherState?.circleSize = (otherState?.circleSize ?: 20f) * scaleFactor
+ }
+ else -> {}
+ }
+ return true
+ }
+ }
+ private val scaleGestureDetector = ScaleGestureDetector(context, scaleGestureListener, null)
+
+ private var touchPaint = Paint()
+ private var stylusPaint = Paint()
+
+ private fun init() {
+ touchPaint.color = Color.RED
+ touchPaint.setStrokeWidth(5f)
+ stylusPaint.color = Color.YELLOW
+ stylusPaint.setStrokeWidth(5f)
+
+ setOnHoverListener { _, event -> processHoverEvent(event); true }
+ }
+
+ private fun processTouchEvent(event: MotionEvent) {
+ scaleGestureDetector.onTouchEvent(event)
+ if (event.actionMasked == ACTION_DOWN) {
+ touchEvents.remove(event.deviceId)
+ myState?.state = PointerState.DOWN
+ } else if (event.actionMasked == ACTION_UP) {
+ myState?.state = PointerState.NONE
+ }
+ var vec = touchEvents.getOrPut(event.deviceId) { Vector<Pair<MotionEvent, Paint>>() }
+
+ val paint = if (event.isFromSource(SOURCE_STYLUS)) {
+ val size = myState?.lineSize ?: 5f
+ stylusPaint.setStrokeWidth(size)
+ Paint(stylusPaint)
+ } else {
+ val size = myState?.lineSize ?: 5f
+ touchPaint.setStrokeWidth(size)
+ Paint(touchPaint)
+ }
+ vec.add(Pair(MotionEvent.obtain(event), paint))
+ invalidate()
+ }
+
+ private fun processHoverEvent(event: MotionEvent) {
+ hoverEvents.remove(event.deviceId)
+ if (event.getActionMasked() != ACTION_HOVER_EXIT) {
+ hoverEvents.put(event.deviceId, MotionEvent.obtain(event))
+ myState?.state = PointerState.HOVER
+ } else {
+ myState?.state = PointerState.NONE
+ }
+ invalidate()
+ }
+
+ public override fun onTouchEvent(event: MotionEvent): Boolean {
+ processTouchEvent(event)
+ return true
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ // Draw touch and stylus MotionEvents
+ for ((_, vec) in touchEvents ) {
+ for (i in 1 until vec.size) {
+ drawLine(canvas, vec[i - 1].first, vec[i].first, vec[i].second)
+ }
+ }
+ // Draw hovers
+ for ((_, event) in hoverEvents ) {
+ if (event.isFromSource(SOURCE_STYLUS)) {
+ val size = myState?.circleSize ?: 20f
+ drawCircle(canvas, event, stylusPaint, size)
+ } else {
+ val size = myState?.circleSize ?: 20f
+ drawCircle(canvas, event, touchPaint, size)
+ }
+ }
+ }
+}
diff --git a/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt b/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt
new file mode 100644
index 000000000000..911208579d4f
--- /dev/null
+++ b/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt
@@ -0,0 +1,83 @@
+/*
+ * 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 test.multideviceinput
+
+import android.app.Activity
+import android.graphics.Color
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets.Type
+import android.view.WindowManager
+
+
+enum class PointerState {
+ DOWN, // One or more pointer(s) down, lines are being drawn
+ HOVER, // Pointer is hovering
+ NONE, // Nothing is touching or hovering
+}
+
+data class SharedScaledPointerSize(
+ var lineSize: Float,
+ var circleSize: Float,
+ var state: PointerState
+)
+
+class MainActivity : Activity() {
+ val TAG = "MultiDeviceInput"
+ private val leftState = SharedScaledPointerSize(5f, 20f, PointerState.NONE)
+ private val rightState = SharedScaledPointerSize(5f, 20f, PointerState.NONE)
+ private lateinit var left: View
+ private lateinit var right: View
+
+ override fun onResume() {
+ super.onResume()
+
+ val wm = getSystemService(WindowManager::class.java)
+ val wmlp = WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION)
+ wmlp.flags = (wmlp.flags or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+
+ val windowMetrics = windowManager.currentWindowMetrics
+ val insets = windowMetrics.windowInsets.getInsetsIgnoringVisibility(Type.systemBars())
+ val width = windowMetrics.bounds.width() - insets.left - insets.right
+ val height = windowMetrics.bounds.height() - insets.top - insets.bottom
+
+ wmlp.width = width * 24 / 50
+ wmlp.height = height * 35 / 50
+
+ val vglp = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+
+ wmlp.setTitle("Left -- " + getPackageName())
+ wmlp.gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ left = DrawingView(this, leftState, rightState)
+ left.setBackgroundColor(Color.LTGRAY)
+ left.setLayoutParams(vglp)
+ wm.addView(left, wmlp)
+
+ wmlp.setTitle("Right -- " + getPackageName())
+ wmlp.gravity = Gravity.CENTER_VERTICAL or Gravity.END
+ right = DrawingView(this, rightState, leftState)
+ right.setBackgroundColor(Color.LTGRAY)
+ right.setLayoutParams(vglp)
+ wm.addView(right, wmlp)
+ }
+}
diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp
index 3505fe1c4afb..38ee8ac99747 100644
--- a/tests/SmokeTestApps/Android.bp
+++ b/tests/SmokeTestApps/Android.bp
@@ -11,4 +11,7 @@ android_test {
name: "SmokeTestTriggerApps",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ errorprone: {
+ enabled: false,
+ },
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index 421ceb797c15..07b733830bd3 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,7 +50,7 @@ import org.junit.runners.JUnit4;
public class VibratorManagerServicePermissionTest {
private static final String PACKAGE_NAME = "com.android.framework.permission.tests";
- private static final int DISPLAY_ID = 1;
+ private static final int DEVICE_ID = 1;
private static final CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -107,7 +107,7 @@ public class VibratorManagerServicePermissionTest {
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -117,7 +117,7 @@ public class VibratorManagerServicePermissionTest {
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -127,7 +127,7 @@ public class VibratorManagerServicePermissionTest {
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -137,7 +137,7 @@ public class VibratorManagerServicePermissionTest {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index fff8f78a5d01..412aa9bf88ab 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -120,6 +120,7 @@ cc_library_host_static {
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
+ "link/FeatureFlagsFilter.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
"link/PrivateAttributeMover.cpp",
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 159c6fd9ef58..c638873873dc 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2514,6 +2514,28 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
}
}
+ // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
+ // values from.
+ std::vector<std::string> all_feature_flags_args;
+ for (const std::string& arg : feature_flags_args_) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
+ context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ all_feature_flags_args.push_back(arg);
+ }
+ }
+
+ for (const std::string& arg : all_feature_flags_args) {
+ if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
+ return 1;
+ }
+ }
+
if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
&options_.stable_id_map)) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index a08f385b2270..26713fd92264 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -17,11 +17,17 @@
#ifndef AAPT2_LINK_H
#define AAPT2_LINK_H
+#include <optional>
#include <regex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
#include "Command.h"
#include "Resource.h"
#include "androidfw/IDiagnostics.h"
+#include "cmd/Util.h"
#include "format/binary/TableFlattener.h"
#include "format/proto/ProtoSerialize.h"
#include "link/ManifestFixer.h"
@@ -72,6 +78,7 @@ struct LinkOptions {
bool use_sparse_encoding = false;
std::unordered_set<std::string> extensions_to_not_compress;
std::optional<std::regex> regex_to_not_compress;
+ FeatureFlagValues feature_flag_values;
// Static lib options.
bool no_static_lib_packages = false;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index a92f24b82547..678d84628015 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -113,6 +113,56 @@ std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std
return std::move(filter);
}
+bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values) {
+ if (arg.empty()) {
+ return true;
+ }
+
+ for (StringPiece flag_and_value : util::Tokenize(arg, ',')) {
+ std::vector<std::string> parts = util::Split(flag_and_value, '=');
+ if (parts.empty()) {
+ continue;
+ }
+
+ if (parts.size() > 2) {
+ diag->Error(android::DiagMessage()
+ << "Invalid feature flag and optional value '" << flag_and_value
+ << "'. Must be in the format 'flag_name[=true|false]");
+ return false;
+ }
+
+ StringPiece flag_name = util::TrimWhitespace(parts[0]);
+ if (flag_name.empty()) {
+ diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
+ return false;
+ }
+
+ std::optional<bool> flag_value = {};
+ if (parts.size() == 2) {
+ StringPiece str_flag_value = util::TrimWhitespace(parts[1]);
+ if (!str_flag_value.empty()) {
+ flag_value = ResourceUtils::ParseBool(parts[1]);
+ if (!flag_value.has_value()) {
+ diag->Error(android::DiagMessage() << "Invalid value for feature flag '" << flag_and_value
+ << "'. Value must be 'true' or 'false'");
+ return false;
+ }
+ }
+ }
+
+ if (auto [it, inserted] =
+ out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
+ !inserted) {
+ // We are allowing the same flag to appear multiple times, last value wins.
+ diag->Note(android::DiagMessage()
+ << "Value for feature flag '" << flag_name << "' was given more than once");
+ it->second = flag_value;
+ }
+ }
+ return true;
+}
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the minSdk. Otherwise the resources that have had
// their SDK version stripped due to minSdk won't ever match.
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 712c07b71695..9ece5dd4d720 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -17,8 +17,13 @@
#ifndef AAPT_SPLIT_UTIL_H
#define AAPT_SPLIT_UTIL_H
+#include <functional>
+#include <map>
+#include <memory>
+#include <optional>
#include <regex>
#include <set>
+#include <string>
#include <unordered_set>
#include "AppInfo.h"
@@ -32,6 +37,8 @@
namespace aapt {
+using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>;
+
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
std::optional<uint16_t> ParseTargetDensityParameter(android::StringPiece arg,
@@ -48,6 +55,13 @@ bool ParseSplitParameter(android::StringPiece arg, android::IDiagnostics* diag,
std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args,
android::IDiagnostics* diag);
+// Parses a feature flags parameter, which can contain one or more pairs of flag names and optional
+// values, and fills in `out_feature_flag_values` with the parsed values. The pairs in the argument
+// are separated by ',' and the name is separated from the value by '=' if there is a value given.
+// Example arg: "flag1=true,flag2=false,flag3=,flag4" where flag3 and flag4 have no given value.
+bool ParseFeatureFlagsParameter(android::StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values);
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the min_sdk. Otherwise the resources that have had
// their SDK version stripped due to min_sdk won't ever match.
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 139bfbcd0f41..723d87ed0af3 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -25,6 +25,7 @@
#include "util/Files.h"
using ::android::ConfigDescription;
+using testing::Pair;
using testing::UnorderedElementsAre;
namespace aapt {
@@ -354,6 +355,51 @@ TEST (UtilTest, ParseSplitParameters) {
EXPECT_CONFIG_EQ(constraints, expected_configuration);
}
+TEST(UtilTest, ParseFeatureFlagsParameter_Empty) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("", diagnostics, &feature_flag_values));
+ EXPECT_TRUE(feature_flag_values.empty());
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_TooManyParts) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=bar=baz", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_NoNameGiven) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,=false", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,bar=42", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(
+ ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values));
+ EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)),
+ Pair("bar", std::optional<bool>(true))));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_Valid) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics,
+ &feature_flag_values));
+ EXPECT_THAT(feature_flag_values,
+ UnorderedElementsAre(Pair("foo", std::optional<bool>(true)),
+ Pair("bar", std::optional<bool>(false)),
+ Pair("baz", std::nullopt), Pair("quux", std::nullopt)));
+}
+
TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp
new file mode 100644
index 000000000000..fdf3f74d4e18
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#include "link/FeatureFlagsFilter.h"
+
+#include <string_view>
+
+#include "androidfw/IDiagnostics.h"
+#include "androidfw/Source.h"
+#include "util/Util.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
+
+using ::aapt::xml::Element;
+using ::aapt::xml::Node;
+using ::aapt::xml::NodeCast;
+
+namespace aapt {
+
+class FlagsVisitor : public xml::Visitor {
+ public:
+ explicit FlagsVisitor(android::IDiagnostics* diagnostics,
+ const FeatureFlagValues& feature_flag_values,
+ const FeatureFlagsFilterOptions& options)
+ : diagnostics_(diagnostics), feature_flag_values_(feature_flag_values), options_(options) {
+ }
+
+ void Visit(xml::Element* node) override {
+ std::erase_if(node->children,
+ [this](std::unique_ptr<xml::Node>& node) { return ShouldRemove(node); });
+ VisitChildren(node);
+ }
+
+ bool HasError() const {
+ return has_error_;
+ }
+
+ private:
+ bool ShouldRemove(std::unique_ptr<xml::Node>& node) {
+ if (const auto* el = NodeCast<Element>(node.get())) {
+ auto* attr = el->FindAttribute(xml::kSchemaAndroid, "featureFlag");
+ if (attr == nullptr) {
+ return false;
+ }
+
+ bool negated = false;
+ std::string_view flag_name = util::TrimWhitespace(attr->value);
+ if (flag_name.starts_with('!')) {
+ negated = true;
+ flag_name = flag_name.substr(1);
+ }
+
+ if (auto it = feature_flag_values_.find(std::string(flag_name));
+ it != feature_flag_values_.end()) {
+ if (it->second.has_value()) {
+ if (options_.remove_disabled_elements) {
+ // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
+ return *it->second == negated;
+ }
+ } else if (options_.flags_must_have_value) {
+ diagnostics_->Error(android::DiagMessage(node->line_number)
+ << "attribute 'android:featureFlag' has flag '" << flag_name
+ << "' without a true/false value from --feature_flags parameter");
+ has_error_ = true;
+ return false;
+ }
+ } else if (options_.fail_on_unrecognized_flags) {
+ diagnostics_->Error(android::DiagMessage(node->line_number)
+ << "attribute 'android:featureFlag' has flag '" << flag_name
+ << "' not found in flags from --feature_flags parameter");
+ has_error_ = true;
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ android::IDiagnostics* diagnostics_;
+ const FeatureFlagValues& feature_flag_values_;
+ const FeatureFlagsFilterOptions& options_;
+ bool has_error_ = false;
+};
+
+bool FeatureFlagsFilter::Consume(IAaptContext* context, xml::XmlResource* doc) {
+ FlagsVisitor visitor(context->GetDiagnostics(), feature_flag_values_, options_);
+ doc->root->Accept(&visitor);
+ return !visitor.HasError();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FeatureFlagsFilter.h b/tools/aapt2/link/FeatureFlagsFilter.h
new file mode 100644
index 000000000000..1d342a71b996
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "cmd/Util.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+struct FeatureFlagsFilterOptions {
+ // If true, elements whose featureFlag values are false (i.e., disabled feature) will be removed.
+ bool remove_disabled_elements = true;
+
+ // If true, `Consume()` will return false (error) if a flag was found that is not in
+ // `feature_flag_values`.
+ bool fail_on_unrecognized_flags = true;
+
+ // If true, `Consume()` will return false (error) if a flag was found whose value in
+ // `feature_flag_values` is not defined (std::nullopt).
+ bool flags_must_have_value = true;
+};
+
+// Looks for the `android:featureFlag` attribute in each XML element, validates the flag names and
+// values, and removes elements according to the values in `feature_flag_values`. An element will be
+// removed if the flag's given value is FALSE. A "!" before the flag name in the attribute indicates
+// a boolean NOT operation, i.e., an element will be removed if the flag's given value is TRUE. For
+// example, if the XML is the following:
+//
+// <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+// <permission android:name="FOO" android:featureFlag="!flag"
+// android:protectionLevel="normal" />
+// <permission android:name="FOO" android:featureFlag="flag"
+// android:protectionLevel="dangerous" />
+// </manifest>
+//
+// If `feature_flag_values` contains {"flag", true}, then the <permission> element with
+// protectionLevel="normal" will be removed, and the <permission> element with
+// protectionLevel="normal" will be kept.
+//
+// The `Consume()` function will return false if there is an invalid flag found (see
+// FeatureFlagsFilterOptions for customizing the filter's validation behavior). Do not use the XML
+// further if there are errors as there may be elements removed already.
+class FeatureFlagsFilter : public IXmlResourceConsumer {
+ public:
+ explicit FeatureFlagsFilter(FeatureFlagValues feature_flag_values,
+ FeatureFlagsFilterOptions options)
+ : feature_flag_values_(std::move(feature_flag_values)), options_(options) {
+ }
+
+ bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FeatureFlagsFilter);
+
+ const FeatureFlagValues feature_flag_values_;
+ const FeatureFlagsFilterOptions options_;
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FeatureFlagsFilter_test.cpp b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
new file mode 100644
index 000000000000..53086cc30f18
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+#include "link/FeatureFlagsFilter.h"
+
+#include <string_view>
+
+#include "test/Test.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace aapt {
+
+// Returns null if there was an error from FeatureFlagsFilter.
+std::unique_ptr<xml::XmlResource> VerifyWithOptions(std::string_view str,
+ const FeatureFlagValues& feature_flag_values,
+ const FeatureFlagsFilterOptions& options) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(str);
+ FeatureFlagsFilter filter(feature_flag_values, options);
+ if (filter.Consume(test::ContextBuilder().Build().get(), doc.get())) {
+ return doc;
+ }
+ return {};
+}
+
+// Returns null if there was an error from FeatureFlagsFilter.
+std::unique_ptr<xml::XmlResource> Verify(std::string_view str,
+ const FeatureFlagValues& feature_flag_values) {
+ return VerifyWithOptions(str, feature_flag_values, {});
+}
+
+TEST(FeatureFlagsFilterTest, NoFeatureFlagAttributes) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+TEST(FeatureFlagsFilterTest, RemoveElementWithDisabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, RemoveElementWithNegatedEnabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="!flag" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, KeepElementWithEnabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, SideBySideEnabledAndDisabled) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="!flag"
+ android:protectionLevel="normal" />
+ <permission android:name="FOO" android:featureFlag="flag"
+ android:protectionLevel="dangerous" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto children = root->GetChildElements();
+ ASSERT_EQ(children.size(), 1);
+ auto attr = children[0]->FindAttribute(xml::kSchemaAndroid, "protectionLevel");
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_EQ(attr->value, "dangerous");
+}
+
+TEST(FeatureFlagsFilterTest, RemoveDeeplyNestedElement) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <application>
+ <provider />
+ <activity>
+ <layout android:featureFlag="!flag" />
+ </activity>
+ </application>
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto application = root->FindChild({}, "application");
+ ASSERT_THAT(application, NotNull());
+ auto activity = application->FindChild({}, "activity");
+ ASSERT_THAT(activity, NotNull());
+ auto maybe_removed = activity->FindChild({}, "layout");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, KeepDeeplyNestedElement) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <application>
+ <provider />
+ <activity>
+ <layout android:featureFlag="flag" />
+ </activity>
+ </application>
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto application = root->FindChild({}, "application");
+ ASSERT_THAT(application, NotNull());
+ auto activity = application->FindChild({}, "activity");
+ ASSERT_THAT(activity, NotNull());
+ auto maybe_removed = activity->FindChild({}, "layout");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnEmptyFeatureFlagAttribute) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag=" " />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnFlagWithNoGivenValue) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnUnrecognizedFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnMultipleValidationErrors) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="bar" />
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionRemoveDisabledElementsIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", false}}, {.remove_disabled_elements = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionFlagsMustHaveValueIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}}, {.flags_must_have_value = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionFailOnUnrecognizedFlagsIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", true}}, {.fail_on_unrecognized_flags = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+} // namespace aapt
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
index e02492d68bb4..192b6f2b5e25 100644
--- a/tools/hoststubgen/TEST_MAPPING
+++ b/tools/hoststubgen/TEST_MAPPING
@@ -1,8 +1,11 @@
{
- // TODO: Change to presubmit.
- "postsubmit": [
+ "presubmit": [
{ "name": "tiny-framework-dump-test" },
{ "name": "hoststubgentest" },
{ "name": "hoststubgen-invoke-test" }
+
+ // As a smoke test.
+ // TODO: Enable it once the infra knows how to run it.
+ // { "name": "CtsUtilTestCasesRavenwood" }
]
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index fd4ec8bd5931..5949bca2f9a3 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -284,6 +284,9 @@ java_library {
"hoststubgen-helper-runtime.ravenwood",
"framework-minus-apex.ravenwood",
],
+ static_libs: [
+ "core-xml-for-device",
+ ],
}
// Defaults for host side test modules.
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
index dab8e8938975..eec72269e0d3 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
@@ -32,5 +32,5 @@ import java.lang.annotation.Target;
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestStaticInitializerStub {
+public @interface HostSideTestStaticInitializerKeep {
}
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
index ff0fe32ad267..493ad56a5cbb 100644
--- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt
+++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
@@ -78,6 +78,9 @@ class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host
class com.android.internal.util.FastPrintWriter keepclass
class com.android.internal.util.LineBreakBufferedWriter keepclass
+class android.util.EventLog stubclass
+class android.util.EventLog !com.android.hoststubgen.nativesubstitution.EventLog_host
+class android.util.EventLog$Event stubclass
# Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of
# its members.
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
new file mode 100644
index 000000000000..292e8da0de10
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
@@ -0,0 +1,69 @@
+/*
+ * 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.nativesubstitution;
+
+import android.util.Log;
+import android.util.Log.Level;
+
+import java.util.Collection;
+
+public class EventLog_host {
+ public static int writeEvent(int tag, int value) {
+ return writeEvent(tag, (Object) value);
+ }
+
+ public static int writeEvent(int tag, long value) {
+ return writeEvent(tag, (Object) value);
+ }
+
+ public static int writeEvent(int tag, float value) {
+ return writeEvent(tag, (Object) value);
+ }
+
+ public static int writeEvent(int tag, String str) {
+ return writeEvent(tag, (Object) str);
+ }
+
+ public static int writeEvent(int tag, Object... list) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("logd: [event] ");
+ final String tagName = android.util.EventLog.getTagName(tag);
+ if (tagName != null) {
+ sb.append(tagName);
+ } else {
+ sb.append(tag);
+ }
+ sb.append(": [");
+ for (int i = 0; i < list.length; i++) {
+ sb.append(String.valueOf(list[i]));
+ if (i < list.length - 1) {
+ sb.append(',');
+ }
+ }
+ sb.append(']');
+ System.out.println(sb.toString());
+ return sb.length();
+ }
+
+ public static void readEvents(int[] tags, Collection<android.util.EventLog.Event> output) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void readEventsOnWrapping(int[] tags, long timestamp,
+ Collection<android.util.EventLog.Event> output) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index 12c7841556fc..4a3a79803b65 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -286,11 +286,15 @@ public class Parcel_host {
}
public static byte[] nativeReadBlob(long nativePtr) {
+ var p = getInstance(nativePtr);
+ if (p.mSize - p.mPos < 4) {
+ // Match native impl that returns "null" when not enough data
+ return null;
+ }
final var size = nativeReadInt(nativePtr);
if (size == -1) {
return null;
}
- var p = getInstance(nativePtr);
try {
p.ensureDataAvailable(align4(size));
} catch (Exception e) {
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 25abbace85de..c770b9ccc800 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -65,7 +65,7 @@ public class HostTestUtils {
*/
public static void onThrowMethodCalled() {
// TODO: Maybe add call tracking?
- throw new AssumptionViolatedException("This method is not supported on the host side");
+ throw new RuntimeException("This method is not supported on the host side");
}
/**
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index 43f9cb9b6b4e..c371b5d54ebd 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -40,5 +40,5 @@
--class-load-hook-annotation
android.hosttest.annotation.HostSideTestClassLoadHook
---stub-static-initializer-annotation
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+--keep-static-initializer-annotation
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 34b2145b8d22..89daa2084420 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -27,8 +27,14 @@ env
# Set up the constants and variables
+# Bazel sets $TEST_TMPDIR.
export TEMP=$TEST_TMPDIR
+if [[ "$TEMP" == "" ]] ; then
+ TEMP=./tmp
+ mkdir -p $TEMP
+fi
+
JAR=hoststubgen-test-tiny-framework.jar
STUB=$TEMP/stub.jar
IMPL=$TEMP/impl.jar
@@ -72,6 +78,26 @@ run_hoststubgen() {
--in-jar $JAR \
--out-stub-jar $STUB \
--out-impl-jar $IMPL \
+ --stub-annotation \
+ android.hosttest.annotation.HostSideTestStub \
+ --keep-annotation \
+ android.hosttest.annotation.HostSideTestKeep \
+ --stub-class-annotation \
+ android.hosttest.annotation.HostSideTestWholeClassStub \
+ --keep-class-annotation \
+ android.hosttest.annotation.HostSideTestWholeClassKeep \
+ --throw-annotation \
+ android.hosttest.annotation.HostSideTestThrow \
+ --remove-annotation \
+ android.hosttest.annotation.HostSideTestRemove \
+ --substitute-annotation \
+ android.hosttest.annotation.HostSideTestSubstitute \
+ --native-substitute-annotation \
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass \
+ --class-load-hook-annotation \
+ android.hosttest.annotation.HostSideTestClassLoadHook \
+ --keep-static-initializer-annotation \
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep \
$filter_arg \
|& tee $HOSTSTUBGEN_OUT
HOSTSTUBGEN_RC=${PIPESTATUS[0]}
@@ -157,6 +183,12 @@ run_hoststubgen_for_failure "One specific class disallowed" \
* # All other classes allowed
"
+run_hoststubgen_for_success "One specific class disallowed, but it doesn't use annotations" \
+ "
+!com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+* # All other classes allowed
+"
+
echo "All tests passed"
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index f32dc721873e..07bd2dc7c867 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -189,7 +189,7 @@ class HostStubGen(val options: HostStubGenOptions) {
options.substituteAnnotations,
options.nativeSubstituteAnnotations,
options.classLoadHookAnnotations,
- options.stubStaticInitializerAnnotations,
+ options.keepStaticInitializerAnnotations,
annotationAllowedClassesFilter,
filter,
)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index aab02b8de254..da5348707528 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -47,7 +47,7 @@ class HostStubGenOptions(
var substituteAnnotations: MutableSet<String> = mutableSetOf(),
var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
- var stubStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
+ var keepStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
@@ -166,8 +166,8 @@ class HostStubGenOptions(
ret.classLoadHookAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
- "--stub-static-initializer-annotation" ->
- ret.stubStaticInitializerAnnotations +=
+ "--keep-static-initializer-annotation" ->
+ ret.keepStaticInitializerAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
"--package-redirect" ->
@@ -318,6 +318,7 @@ class HostStubGenOptions(
substituteAnnotations=$substituteAnnotations,
nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
classLoadHookAnnotations=$classLoadHookAnnotations,
+ keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
packageRedirects=$packageRedirects,
$annotationAllowedClassesFile=$annotationAllowedClassesFile,
defaultClassLoadHook=$defaultClassLoadHook,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 1bcf3642082f..d7aa0af13ce2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -119,8 +119,12 @@ fun getDirectOuterClassName(className: String): String? {
* Write bytecode to push all the method arguments to the stack.
* The number of arguments and their type are taken from [methodDescriptor].
*/
-fun writeByteCodeToPushArguments(methodDescriptor: String, writer: MethodVisitor) {
- var i = -1
+fun writeByteCodeToPushArguments(
+ methodDescriptor: String,
+ writer: MethodVisitor,
+ argOffset: Int = 0,
+ ) {
+ var i = argOffset - 1
Type.getArgumentTypes(methodDescriptor).forEach { type ->
i++
@@ -159,6 +163,18 @@ fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) {
}
/**
+ * Given a method descriptor, insert an [argType] as the first argument to it.
+ */
+fun prependArgTypeToMethodDescriptor(methodDescriptor: String, argType: Type): String {
+ val returnType = Type.getReturnType(methodDescriptor)
+ val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
+
+ argTypes.add(0, argType)
+
+ return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
+}
+
+/**
* Return the "visibility" modifier from an `access` integer.
*
* (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 9bb5381eef06..248121c63d78 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -52,7 +52,7 @@ class AnnotationBasedFilter(
substituteAnnotations_: Set<String>,
nativeSubstituteAnnotations_: Set<String>,
classLoadHookAnnotations_: Set<String>,
- stubStaticInitializerAnnotations_: Set<String>,
+ keepStaticInitializerAnnotations_: Set<String>,
private val annotationAllowedClassesFilter: ClassFilter,
fallback: OutputFilter,
) : DelegatingFilter(fallback) {
@@ -65,8 +65,8 @@ class AnnotationBasedFilter(
private var substituteAnnotations = convertToInternalNames(substituteAnnotations_)
private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
- private var stubStaticInitializerAnnotations =
- convertToInternalNames(stubStaticInitializerAnnotations_)
+ private var keepStaticInitializerAnnotations =
+ convertToInternalNames(keepStaticInitializerAnnotations_)
/** Annotations that control API visibility. */
private var visibilityAnnotations: Set<String> = convertToInternalNames(
@@ -133,6 +133,25 @@ class AnnotationBasedFilter(
}
}
+ fun findAnyAnnotation(
+ className: String,
+ anyAnnotations: Set<String>,
+ visibleAnnotations: List<AnnotationNode>?,
+ invisibleAnnotations: List<AnnotationNode>?,
+ ): AnnotationNode? {
+ val ret = findAnyAnnotation(anyAnnotations, visibleAnnotations, invisibleAnnotations)
+
+ if (ret != null) {
+ if (!annotationAllowedClassesFilter.matches(className)) {
+ throw InvalidAnnotationException(
+ "Class ${className.toHumanReadableClassName()} is not allowed to have " +
+ "Ravenwood annotations. Contact g/ravenwood for more details.")
+ }
+ }
+
+ return ret
+ }
+
/**
* Find a visibility annotation.
*
@@ -149,28 +168,22 @@ class AnnotationBasedFilter(
): FilterPolicyWithReason? {
detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3)
- if (!annotationAllowedClassesFilter.matches(className)) {
- throw InvalidAnnotationException(
- "Class ${className.toHumanReadableClassName()} is not allowed to have " +
- "Ravenwood annotations. Contact g/ravenwood for more details.")
- }
-
- findAnyAnnotation(stubAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, stubAnnotations, visibles, invisibles)?.let {
return FilterPolicy.Stub.withReason(reasonAnnotation)
}
- findAnyAnnotation(stubClassAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, stubClassAnnotations, visibles, invisibles)?.let {
return FilterPolicy.StubClass.withReason(reasonClassAnnotation)
}
- findAnyAnnotation(keepAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, keepAnnotations, visibles, invisibles)?.let {
return FilterPolicy.Keep.withReason(reasonAnnotation)
}
- findAnyAnnotation(keepClassAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, keepClassAnnotations, visibles, invisibles)?.let {
return FilterPolicy.KeepClass.withReason(reasonClassAnnotation)
}
- findAnyAnnotation(throwAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, throwAnnotations, visibles, invisibles)?.let {
return FilterPolicy.Throw.withReason(reasonAnnotation)
}
- findAnyAnnotation(removeAnnotations, visibles, invisibles)?.let {
+ findAnyAnnotation(className, removeAnnotations, visibles, invisibles)?.let {
return FilterPolicy.Remove.withReason(reasonAnnotation)
}
@@ -227,9 +240,9 @@ class AnnotationBasedFilter(
val cn = classes.getClass(className)
if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
- findAnyAnnotation(stubStaticInitializerAnnotations,
+ findAnyAnnotation(cn.name, keepStaticInitializerAnnotations,
cn.visibleAnnotations, cn.invisibleAnnotations)?.let {
- return FilterPolicy.Stub.withReason(reasonAnnotation)
+ return FilterPolicy.Keep.withReason(reasonAnnotation)
}
}
@@ -364,7 +377,7 @@ class AnnotationBasedFilter(
throw HostStubGenInternalException("Policy $policy shouldn't show up here")
}
- val suffix = getAnnotationField(an, "suffix") ?: return@let
+ val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
val renameFrom = mn.name + suffix
val renameTo = mn.name
@@ -374,13 +387,17 @@ class AnnotationBasedFilter(
}
// This mn has "SubstituteWith". This means,
- // 1. Re move the "rename-to" method, so add it to substitutedMethods.
+ // 1. Re move the "rename-to" method, so add it to substitutedMethods.
policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
FilterPolicy.Remove.withReason("substitute-to")
+ // If the policy is "stub", use "stub".
+ // Otherwise, it must be "keep" or "throw", but there's no point in using
+ // "throw", so let's use "keep".
+ val newPolicy = if (policy.needsInStub) policy else FilterPolicy.Keep
// 2. We also keep the from-to in the map.
policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
- policy.withReason("substitute-from")
+ newPolicy.withReason("substitute-from")
substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
@@ -392,10 +409,11 @@ class AnnotationBasedFilter(
/**
* Return the (String) value of 'value' parameter from an annotation.
*/
- private fun getAnnotationField(an: AnnotationNode, name: String): String? {
+ private fun getAnnotationField(an: AnnotationNode, name: String,
+ required: Boolean = true): String? {
try {
val suffix = findAnnotationValueAsString(an, name)
- if (suffix == null) {
+ if (suffix == null && required) {
errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
}
return suffix
@@ -425,4 +443,4 @@ class AnnotationBasedFilter(
return ret
}
}
-} \ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 9c0fa69f8a14..84856aca84cc 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -25,6 +25,7 @@ import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.isAnnotation
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
+import com.android.hoststubgen.asm.isSynthetic
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
import org.objectweb.asm.tree.ClassNode
@@ -33,6 +34,8 @@ import org.objectweb.asm.tree.ClassNode
* - "keep all anonymous inner classes if the outer class is keep".
* (But anonymous inner classes should never be in "stub")
* - For classes in stub, make sure private parameterless constructors are also in stub, if any.
+ *
+ * TODO: Do we need a way to make anonymous class methods and lambdas "throw"?
*/
class ImplicitOutputFilter(
private val errors: HostStubGenErrors,
@@ -74,10 +77,11 @@ class ImplicitOutputFilter(
descriptor: String
): FilterPolicyWithReason {
val fallback = super.getPolicyForMethod(className, methodName, descriptor)
+ val classPolicy = outermostFilter.getPolicyForClass(className)
// If the class is in the stub, then we need to put the private constructor in the stub too,
// to prevent the class from getting instantiated.
- if (outermostFilter.getPolicyForClass(className).policy.needsInStub &&
+ if (classPolicy.policy.needsInStub &&
!fallback.policy.needsInStub &&
(methodName == "<init>") && // Constructor?
(descriptor == "()V")) { // Has zero parameters?
@@ -102,8 +106,6 @@ class ImplicitOutputFilter(
" [original throw reason: ${fallback.reason}]")
}
- val classPolicy = super.getPolicyForClass(className)
-
log.d("Class ${cn.name} Class policy: $classPolicy")
if (classPolicy.policy.needsInImpl) {
// Do it only when the class needs to be kept...
@@ -111,9 +113,11 @@ class ImplicitOutputFilter(
// Member policy should be "keep" or "stub".
val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+ val mn = classes.findMethod(className, methodName, descriptor)
+
// Keep (or stub) the generated enum members.
if (cn.isEnum()) {
- classes.findMethod(className, methodName, descriptor)?.let { mn ->
+ mn?.let { mn ->
if (isAutoGeneratedEnumMember(mn)) {
return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
}
@@ -124,6 +128,15 @@ class ImplicitOutputFilter(
if (cn.isAnnotation()) {
return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
}
+
+ mn?.let {
+ if (mn.isSynthetic()) {
+ // For synthetic methods (such as lambdas), let's just inherit the class's
+ // policy.
+ return memberPolicy.withReason(classPolicy.reason).wrapReason(
+ "synthetic method")
+ }
+ }
}
return fallback
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index e63efd0e43ba..88db15b86143 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -19,6 +19,7 @@ import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
+import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
import com.android.hoststubgen.asm.writeByteCodeToPushArguments
import com.android.hoststubgen.asm.writeByteCodeToReturn
import com.android.hoststubgen.filters.FilterPolicy
@@ -285,7 +286,7 @@ class ImplGeneratingAdapter(
* class.
*/
private inner class NativeSubstitutingMethodAdapter(
- access: Int,
+ val access: Int,
private val name: String,
private val descriptor: String,
signature: String?,
@@ -300,12 +301,33 @@ class ImplGeneratingAdapter(
}
override fun visitEnd() {
- writeByteCodeToPushArguments(descriptor, this)
+ var targetDescriptor = descriptor
+ var argOffset = 0
+
+ // For non-static native method, we need to tweak it a bit.
+ if ((access and Opcodes.ACC_STATIC) == 0) {
+ // Push `this` as the first argument.
+ this.visitVarInsn(Opcodes.ALOAD, 0)
+
+ // Update the descriptor -- add this class's type as the first argument
+ // to the method descriptor.
+ val thisType = Type.getType("L" + currentClassName + ";")
+
+ targetDescriptor = prependArgTypeToMethodDescriptor(
+ descriptor,
+ thisType,
+ )
+
+ // Shift the original arguments by one.
+ argOffset = 1
+ }
+
+ writeByteCodeToPushArguments(descriptor, this, argOffset)
visitMethodInsn(Opcodes.INVOKESTATIC,
nativeSubstitutionClass,
name,
- descriptor,
+ targetDescriptor,
false)
writeByteCodeToReturn(descriptor, this)
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 639fb16c4251..00cbfe38e815 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -117,7 +117,7 @@ fi
if (( $two_way )) ; then
echo "# Running meld..."
- run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]}
+ run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]} --diff ${files[0]} ${files[2]}
fi
if (( $any_file_changed == 0 )) ; then
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 78a4fa692c21..673d3e8e25e9 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
@@ -84,17 +84,17 @@ RuntimeVisibleAnnotations:
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestStaticInitializerStub.class
- Compiled from "HostSideTestStaticInitializerStub.java"
-public interface android.hosttest.annotation.HostSideTestStaticInitializerStub extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+ Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerStub
+ this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
}
-SourceFile: "HostSideTestStaticInitializerStub.java"
+SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
@@ -878,7 +878,7 @@ RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1401,6 +1401,315 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
}
SourceFile: "TinyFrameworkForTextPolicy.java"
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -1409,7 +1718,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 2
+ interfaces: 0, fields: 1, methods: 8, attributes: 2
+ int value;
+ descriptor: I
+ flags: (0x0000)
+
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1458,6 +1771,40 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
Start Length Slot Name Signature
0 6 0 arg1 J
0 6 2 arg2 J
+
+ public void setValue(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 6 1 v I
+
+ public native int nativeNonStaticAddToValue(int);
+ descriptor: (I)I
+ flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 6 1 arg I
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
@@ -1473,9 +1820,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 2
+ interfaces: 0, fields: 0, methods: 4, attributes: 2
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1517,6 +1864,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
Start Length Slot Name Signature
0 4 0 arg1 J
0 4 2 arg2 J
+
+ public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+ x: iload_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 7 1 arg I
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
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 df63815ea0ae..d12588ae4ce2 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
@@ -405,7 +405,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 3
+ interfaces: 0, fields: 2, methods: 0, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -420,16 +420,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
x: #x()
android.hosttest.annotation.HostSideTestStub
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
@@ -445,7 +435,7 @@ RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -784,6 +774,263 @@ RuntimeVisibleAnnotations:
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -792,7 +1039,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 1, methods: 8, attributes: 3
+ int value;
+ descriptor: I
+ flags: (0x0000)
+
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -833,6 +1084,32 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
+
+ public void setValue(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public native int nativeNonStaticAddToValue(int);
+ descriptor: (I)I
+ flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
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 2218d8d0b2e1..97fb64f38b2d 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
@@ -797,7 +797,7 @@ RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1323,6 +1323,325 @@ RuntimeVisibleAnnotations:
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -1331,7 +1650,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 1, methods: 8, attributes: 3
+ int value;
+ descriptor: I
+ flags: (0x0000)
+
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1391,6 +1714,46 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
Start Length Slot Name Signature
0 6 0 arg1 J
0 6 2 arg2 J
+
+ public void setValue(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 6 1 v I
+
+ public int nativeNonStaticAddToValue(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ireturn
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 0 6 1 arg I
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -1413,7 +1776,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -1473,6 +1836,28 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
Start Length Slot Name Signature
15 4 0 arg1 J
15 4 2 arg2 J
+
+ public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeNonStaticAddToValue
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+ x: iload_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 15 7 1 arg I
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
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 df63815ea0ae..d12588ae4ce2 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
@@ -405,7 +405,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 3
+ interfaces: 0, fields: 2, methods: 0, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -420,16 +420,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
x: #x()
android.hosttest.annotation.HostSideTestStub
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
@@ -445,7 +435,7 @@ RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -784,6 +774,263 @@ RuntimeVisibleAnnotations:
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -792,7 +1039,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 1, methods: 8, attributes: 3
+ int value;
+ descriptor: I
+ flags: (0x0000)
+
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -833,6 +1084,32 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
+
+ public void setValue(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public native int nativeNonStaticAddToValue(int);
+ descriptor: (I)I
+ flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
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 3ac9c6a816f1..8035189b50c9 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
@@ -1045,7 +1045,7 @@ RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1695,6 +1695,411 @@ RuntimeVisibleAnnotations:
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$getSupplier_static$3
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$getSupplier$2
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$static$1
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$new$0
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$getSupplier_static$3
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$getSupplier$2
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$static$1
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$new$0
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -1703,7 +2108,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 6, attributes: 3
+ interfaces: 0, fields: 1, methods: 9, attributes: 3
+ int value;
+ descriptor: I
+ flags: (0x0000)
+
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -1788,6 +2197,56 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
Start Length Slot Name Signature
11 6 0 arg1 J
11 6 2 arg2 J
+
+ public void setValue(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String setValue
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 11 6 1 v I
+
+ public int nativeNonStaticAddToValue(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ireturn
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeNonStaticAddToValue_should_be_like_this
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 11 6 1 arg I
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -1810,7 +2269,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 3
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -1895,6 +2354,33 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
Start Length Slot Name Signature
26 4 0 arg1 J
26 4 2 arg2 J
+
+ public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeNonStaticAddToValue
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeNonStaticAddToValue
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+ x: iload_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+ 26 7 1 arg I
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
index 998acf242d85..ea1ad93b21b4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
@@ -16,14 +16,13 @@
package com.android.hoststubgen.test.tinyframework;
import android.hosttest.annotation.HostSideTestClassLoadHook;
-import android.hosttest.annotation.HostSideTestStaticInitializerStub;
+import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
@HostSideTestStub
-@HostSideTestStaticInitializerStub
+@HostSideTestStaticInitializerKeep
public class TinyFrameworkClassWithInitializerStub {
static {
sInitialized = true;
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
new file mode 100644
index 000000000000..0d1203b0dedc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
@@ -0,0 +1,77 @@
+/*
+ * 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.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
+import android.hosttest.annotation.HostSideTestStub;
+
+import java.util.function.Supplier;
+
+
+/**
+ * In this class, we explicitly mark each member as "stub". (rather than using WholeClassStub)
+ *
+ * This means the actual generated lambda functions would be removed by default.
+ *
+ * Implicit filter should take care of them.
+ */
+@HostSideTestStub
+@HostSideTestStaticInitializerKeep
+public class TinyFrameworkLambdas {
+ @HostSideTestStub
+ public TinyFrameworkLambdas() {
+ }
+
+ @HostSideTestStub
+ public final Supplier<Integer> mSupplier = () -> 1;
+
+ @HostSideTestStub
+ public static final Supplier<Integer> sSupplier = () -> 2;
+
+ @HostSideTestStub
+ public Supplier<Integer> getSupplier() {
+ return () -> 3;
+ }
+
+ @HostSideTestStub
+ public static Supplier<Integer> getSupplier_static() {
+ return () -> 4;
+ }
+
+ @HostSideTestStub
+ @HostSideTestStaticInitializerKeep
+ public static class Nested {
+ @HostSideTestStub
+ public Nested() {
+ }
+
+ @HostSideTestStub
+ public final Supplier<Integer> mSupplier = () -> 5;
+
+ @HostSideTestStub
+ public static final Supplier<Integer> sSupplier = () -> 6;
+
+ @HostSideTestStub
+ public Supplier<Integer> getSupplier() {
+ return () -> 7;
+ }
+
+ @HostSideTestStub
+ public static Supplier<Integer> getSupplier_static() {
+ return () -> 8;
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index c151dcc9c90e..e7b5d9fc2ece 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -32,4 +32,16 @@ public class TinyFrameworkNative {
public static long nativeLongPlus_should_be_like_this(long arg1, long arg2) {
return TinyFrameworkNative_host.nativeLongPlus(arg1, arg2);
}
+
+ int value;
+
+ public void setValue(int v) {
+ this.value = v;
+ }
+
+ public native int nativeNonStaticAddToValue(int arg);
+
+ public int nativeNonStaticAddToValue_should_be_like_this(int arg) {
+ return TinyFrameworkNative_host.nativeNonStaticAddToValue(this, arg);
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index 48f7dea8c66c..749ebaa378e3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -28,4 +28,10 @@ public class TinyFrameworkNative_host {
public static long nativeLongPlus(long arg1, long arg2) {
return arg1 + arg2;
}
+
+ // Note, the method must be static even for a non-static native method, but instead it
+ // must take the "source" instance as the first argument.
+ public static int nativeNonStaticAddToValue(TinyFrameworkNative source, int arg) {
+ return source.value + arg;
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
new file mode 100644
index 000000000000..76bbcad2ab26
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.tinyframework;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * We don't want to use any android classes in this module, so we create our own copy of it here.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface LargeTest {}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
index 6b5110ef2cef..d57735b1987c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
@@ -22,9 +22,10 @@ import java.text.DecimalFormat;
/**
* Contains simple micro-benchmarks.
*/
+@LargeTest
public class TinyFrameworkBenchmark {
private static final int MINIMAL_ITERATION = 1000;
- private static final int MEASURE_SECONDS = 3;
+ private static final int MEASURE_SECONDS = 1;
private static final DecimalFormat sFormatter = new DecimalFormat("#,###");
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index ecb181ba4bbf..d04ca52a5682 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -107,11 +107,58 @@ public class TinyFrameworkClassTest {
}
@Test
+ public void testLambda1() {
+ assertThat(new TinyFrameworkLambdas().mSupplier.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void testLambda2() {
+ assertThat(TinyFrameworkLambdas.sSupplier.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void testLambda3() {
+ assertThat(new TinyFrameworkLambdas().getSupplier().get()).isEqualTo(3);
+ }
+
+ @Test
+ public void testLambda4() {
+ assertThat(TinyFrameworkLambdas.getSupplier_static().get()).isEqualTo(4);
+ }
+
+ @Test
+ public void testLambda5() {
+ assertThat(new TinyFrameworkLambdas.Nested().mSupplier.get()).isEqualTo(5);
+ }
+
+ @Test
+ public void testLambda6() {
+ assertThat(TinyFrameworkLambdas.Nested.sSupplier.get()).isEqualTo(6);
+ }
+
+ @Test
+ public void testLambda7() {
+ assertThat(new TinyFrameworkLambdas.Nested().getSupplier().get()).isEqualTo(7);
+ }
+
+ @Test
+ public void testLambda8() {
+ assertThat(TinyFrameworkLambdas.Nested.getSupplier_static().get()).isEqualTo(8);
+ }
+
+ @Test
public void testNativeSubstitutionClass() {
assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5);
}
@Test
+ public void testNativeSubstitutionClass_nonStatic() {
+ TinyFrameworkNative instance = new TinyFrameworkNative();
+ instance.setValue(5);
+ assertThat(instance.nativeNonStaticAddToValue(3)).isEqualTo(8);
+ }
+
+ @Test
public void testExitLog() {
thrown.expect(RuntimeException.class);
thrown.expectMessage("Outer exception");
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 4afa2d7a659a..2dac08969d44 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -53,4 +53,6 @@ run ./scripts/build-framework-hostside-jars-and-extract.sh
# These tests should all pass.
run-ravenwood-test ${READY_TEST_MODULES[*]}
+run atest CtsUtilTestCasesRavenwood
+
echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!" \ No newline at end of file
diff --git a/tools/lint/README.md b/tools/lint/README.md
index b235ad60c799..ff8e44229189 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -103,10 +103,15 @@ out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/and
As noted above, this baseline file contains warnings too, which might be undesirable. For example,
CI tools might surface these warnings in code reviews. In order to create this file without
-warnings, we need to pass another flag to lint: `--nowarn`. The easiest way to do this is to
-locally change the soong code in
-[lint.go](http://cs/aosp-master/build/soong/java/lint.go;l=451;rcl=2e778d5bc4a8d1d77b4f4a3029a4a254ad57db75)
-adding `cmd.Flag("--nowarn")` and running lint again.
+warnings, we need to pass another flag to lint: `--nowarn`. One option is to add the flag to your
+Android.bp file and then run lint again:
+
+```
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ flags: ["--nowarn"],
+ }
+```
# Documentation
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index d41fee3fc0dc..24d203fd1116 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -24,33 +24,31 @@ import com.intellij.psi.PsiReferenceList
import org.jetbrains.uast.UMethod
/**
- * Given a UMethod, determine if this method is
- * the entrypoint to an interface generated by AIDL,
- * returning the interface name if so, otherwise returning null
+ * Given a UMethod, determine if this method is the entrypoint to an interface
+ * generated by AIDL, returning the interface name if so, otherwise returning
+ * null
*/
fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? {
- if (!isContainedInSubclassOfStub(context, node)) return null
- for (superMethod in node.findSuperMethods()) {
- for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
- ?: continue) {
- if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
- return superMethod.containingClass?.name
- }
- }
- }
- return null
+ val containingStub = containingStub(context, node) ?: return null
+ val superMethod = node.findSuperMethods(containingStub)
+ if (superMethod.isEmpty()) return null
+ return containingStub.containingClass?.name
}
-fun isContainedInSubclassOfStub(context: JavaContext, node: UMethod?): Boolean {
+/* Returns the containing Stub class if any. This is not sufficient to infer
+ * that the method itself extends an AIDL generated method. See
+ * getContainingAidlInterface for that purpose.
+ */
+fun containingStub(context: JavaContext, node: UMethod?): PsiClass? {
var superClass = node?.containingClass?.superClass
while (superClass != null) {
- if (isStub(context, superClass)) return true
+ if (isStub(context, superClass)) return superClass
superClass = superClass.superClass
}
- return false
+ return null
}
-fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
+private fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
if (psiClass == null) return false
if (psiClass.name != "Stub") return false
if (!context.evaluator.isStatic(psiClass)) return false
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 935badecf8d5..624a1987638e 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -20,6 +20,7 @@ import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.parcel.SaferParcelChecker
+import com.google.android.lint.aidl.PermissionAnnotationDetector
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@@ -37,6 +38,7 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() {
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
// TODO: Currently crashes due to OOM issue
// PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
new file mode 100644
index 000000000000..6b50cfd9e5ab
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
@@ -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.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+/**
+ * Ensures all AIDL-generated methods are annotated.
+ *
+ * This detector is run on system_server to validate that any method that may
+ * be exposed via an AIDL interface is permission-annotated. That is, it must
+ * have one of the following annotation:
+ * - @EnforcePermission
+ * - @RequiresNoPermission
+ * - @PermissionManuallyEnforced
+ */
+class PermissionAnnotationDetector : AidlImplementationDetector() {
+
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ if (context.evaluator.isAbstract(node)) return
+
+ if (AIDL_PERMISSION_ANNOTATIONS.any { node.hasAnnotation(it) }) return
+
+ context.report(
+ ISSUE_MISSING_PERMISSION_ANNOTATION,
+ node,
+ context.getLocation(node),
+ "The method ${node.name} is not permission-annotated."
+ )
+ }
+
+ companion object {
+
+ private val EXPLANATION_MISSING_PERMISSION_ANNOTATION = """
+ Interfaces that are exposed by system_server are required to have an annotation which
+ denotes the type of permission enforced. There are 3 possible options:
+ - @EnforcePermission
+ - @RequiresNoPermission
+ - @PermissionManuallyEnforced
+ See the documentation of each annotation for further details.
+
+ The annotation on the Java implementation must be the same that the AIDL interface
+ definition. This is verified by a lint in the build system.
+ """.trimIndent()
+
+ @JvmField
+ val ISSUE_MISSING_PERMISSION_ANNOTATION = Issue.create(
+ id = "MissingPermissionAnnotation",
+ briefDescription = "No permission annotation on exposed AIDL interface.",
+ explanation = EXPLANATION_MISSING_PERMISSION_ANNOTATION,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ PermissionAnnotationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ enabledByDefault = false
+ )
+ }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
new file mode 100644
index 000000000000..bce848a2e3a7
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class PermissionAnnotationDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = PermissionAnnotationDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission("android.Manifest.permission.READ_CONTACTS")
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMissingAnnotation() {
+ lint().files(
+ java(
+ """
+ public class Bar extends IBar.Stub {
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Bar.java:2: Error: The method testMethod is not permission-annotated. [MissingPermissionAnnotation]
+ public void testMethod() { }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testNoIssueWhenExtendingWithAnotherSubclass() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() { }
+ // not an AIDL method, just another method
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ java(
+ """
+ public class Baz extends Bar {
+ @Override
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /* Stubs */
+
+ // A service with permission annotation on the method.
+ private val interfaceIFoo: TestFile = java(
+ """
+ public interface IFoo extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFoo {
+ }
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ @Override
+ @android.annotation.RequiresNoPermission
+ public void testMethodNoPermission();
+ @Override
+ @android.annotation.PermissionManuallyEnforced
+ public void testMethodManual();
+ }
+ """
+ ).indented()
+
+ // A service with no permission annotation.
+ private val interfaceIBar: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFoo, interfaceIBar)
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 83b8f163abee..4455a9cda3a8 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -168,7 +168,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
annotationInfo.origin == AnnotationOrigin.METHOD) {
/* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
val uMethod = element as? UMethod ?: return
- if (!isContainedInSubclassOfStub(context, uMethod)) {
+ if (getContainingAidlInterface(context, uMethod) == null) {
return
}
val overridingMethod = element.sourcePsi as PsiMethod
@@ -184,7 +184,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
if (context.evaluator.isAbstract(node)) return
if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
- if (!isContainedInSubclassOfStub(context, node)) {
+ val stubClass = containingStub(context, node)
+ if (stubClass == null) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
node,
@@ -196,7 +197,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
/* Check that we are connected to the super class */
val overridingMethod = node as PsiMethod
- val parents = overridingMethod.findSuperMethods()
+ val parents = overridingMethod.findSuperMethods(stubClass)
if (parents.isEmpty()) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index d8afcb977594..2afca05f8130 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -176,6 +176,29 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
""".addLineContinuation())
}
+ fun testDetectNoIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass43 extends IFooMethod.Stub {
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(), java(
+ """
+ package test.pkg;
+ public class TestClass44 extends TestClass43 {
+ @Override
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
fun testDetectIssuesEmptyAnnotationOnMethod() {
lint().files(java(
"""
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index ebda6f1c5826..4f5e0e48c793 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -70,14 +70,8 @@ public abstract class SharedConnectivityService extends Service {
private List<HotspotNetwork> mHotspotNetworks = Collections.emptyList();
private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
private SharedConnectivitySettingsState mSettingsState = null;
- private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus =
- new HotspotNetworkConnectionStatus.Builder()
- .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
- .setExtras(Bundle.EMPTY).build();
- private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus =
- new KnownNetworkConnectionStatus.Builder()
- .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
- .setExtras(Bundle.EMPTY).build();
+ private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus = null;
+ private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus = null;
// Used for testing
private CountDownLatch mCountDownLatch;
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index c6f67987746a..48ac82dc54a8 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -394,6 +394,26 @@ public class SharedConnectivityServiceTest {
verify(mCallback, never()).onKnownNetworkConnectionStatusChanged(any());
}
+ @Test
+ public void getHotspotNetworkConnectionStatus_withoutUpdate_returnsNull()
+ throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ assertThat(binder.getHotspotNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getKnownNetworkConnectionStatus_withoutUpdate_returnsNull()
+ throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ assertThat(binder.getKnownNetworkConnectionStatus()).isNull();
+ }
+
private FakeSharedConnectivityService createService() {
FakeSharedConnectivityService service = new FakeSharedConnectivityService();
service.attachBaseContext(mContext);